From b7961203c4ed1e071fa5a4d0c31cf034571191ba Mon Sep 17 00:00:00 2001 From: Gantigmaa Selenge <39860586+tinaselenge@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:44:19 +0000 Subject: [PATCH] Remove Vertx from KafkaConnect API (#10911) Signed-off-by: Gantigmaa Selenge --- .../assembly/AbstractConnectOperator.java | 46 +- .../operator/assembly/KafkaConnectApi.java | 54 +- .../assembly/KafkaConnectApiImpl.java | 907 ++++++++---------- .../KafkaConnectAssemblyOperator.java | 8 +- .../KafkaMirrorMaker2AssemblyOperator.java | 6 +- .../operator/assembly/ConnectorMockTest.java | 83 +- .../operator/assembly/KafkaConnectApiIT.java | 229 ++--- .../assembly/KafkaConnectApiImplTest.java | 149 +-- .../assembly/KafkaConnectApiMockTest.java | 101 +- ...emblyOperatorConnectorAutoRestartTest.java | 5 +- ...tAssemblyOperatorConnectorOffsetsTest.java | 45 +- .../KafkaConnectAssemblyOperatorMockTest.java | 9 +- ...afkaConnectAssemblyOperatorPodSetTest.java | 25 +- .../operator/assembly/KafkaConnectorIT.java | 22 +- ...emblyOperatorConnectorAutoRestartTest.java | 14 +- ...2AssemblyOperatorConnectorOffsetsTest.java | 45 +- ...aMirrorMaker2AssemblyOperatorMockTest.java | 29 +- ...irrorMaker2AssemblyOperatorPodSetTest.java | 57 +- 18 files changed, 868 insertions(+), 966 deletions(-) diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java index 4eb180fddbb..1cb86cc1a72 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java @@ -41,6 +41,7 @@ import io.strimzi.operator.cluster.model.RestartReason; import io.strimzi.operator.cluster.model.RestartReasons; import io.strimzi.operator.cluster.model.SharedEnvironmentProvider; +import io.strimzi.operator.cluster.operator.VertxUtil; import io.strimzi.operator.cluster.operator.resource.ResourceOperatorSupplier; import io.strimzi.operator.cluster.operator.resource.kubernetes.ClusterRoleBindingOperator; import io.strimzi.operator.cluster.operator.resource.kubernetes.ConfigMapOperator; @@ -355,7 +356,7 @@ protected Future connectPodDisruptionBudget(Reconciliation reconciliation, */ protected Future reconcileConnectLoggers(Reconciliation reconciliation, String host, String desiredLogging, OrderedProperties defaultLogging) { KafkaConnectApi apiClient = connectClientProvider.apply(vertx); - return apiClient.updateConnectLoggers(reconciliation, host, port, desiredLogging, defaultLogging) + return VertxUtil.completableFutureToVertxFuture(apiClient.updateConnectLoggers(reconciliation, host, port, desiredLogging, defaultLogging)) .compose(updated -> { if (Boolean.TRUE.equals(updated)) { LOGGER.infoCr(reconciliation, "Logging configuration was updated"); @@ -383,17 +384,16 @@ protected Future maybeCreateOrUpdateConnector(Reco String connectorName, KafkaConnectorSpec connectorSpec, CustomResource resource) { KafkaConnectorConfiguration desiredConfig = new KafkaConnectorConfiguration(reconciliation, connectorSpec.getConfig().entrySet()); - return apiClient.getConnectorConfig(reconciliation, new BackOff(200L, 2, 6), host, port, connectorName).compose( + return VertxUtil.completableFutureToVertxFuture(apiClient.getConnectorConfig(reconciliation, new BackOff(200L, 2, 6), host, port, connectorName)).compose( currentConfig -> { if (!needsReconfiguring(reconciliation, connectorName, connectorSpec, desiredConfig.asOrderedProperties().asMap(), currentConfig)) { LOGGER.debugCr(reconciliation, "Connector {} exists and has desired config, {}=={}", connectorName, desiredConfig.asOrderedProperties().asMap(), currentConfig); - return apiClient.status(reconciliation, host, port, connectorName) + return VertxUtil.completableFutureToVertxFuture(apiClient.status(reconciliation, host, port, connectorName)) .compose(status -> updateState(reconciliation, host, apiClient, connectorName, connectorSpec, status, new ArrayList<>())) .compose(conditions -> manageConnectorOffsets(reconciliation, host, apiClient, connectorName, resource, connectorSpec, conditions)) .compose(conditions -> maybeRestartConnector(reconciliation, host, apiClient, connectorName, resource, conditions)) .compose(conditions -> maybeRestartConnectorTask(reconciliation, host, apiClient, connectorName, resource, conditions)) - .compose(conditions -> - apiClient.statusWithBackOff(reconciliation, new BackOff(200L, 2, 10), host, port, connectorName) + .compose(conditions -> VertxUtil.completableFutureToVertxFuture(apiClient.statusWithBackOff(reconciliation, new BackOff(200L, 2, 10), host, port, connectorName)) .compose(createConnectorStatusAndConditions(conditions))) .compose(status -> autoRestartFailedConnectorAndTasks(reconciliation, host, apiClient, connectorName, connectorSpec, status, resource)) .compose(status -> updateConnectorTopics(reconciliation, host, apiClient, connectorName, status)); @@ -440,11 +440,11 @@ private boolean needsReconfiguring(Reconciliation reconciliation, String connect private Future> createOrUpdateConnector(Reconciliation reconciliation, String host, KafkaConnectApi apiClient, String connectorName, KafkaConnectorSpec connectorSpec, KafkaConnectorConfiguration desiredConfig) { - return apiClient.createOrUpdatePutRequest(reconciliation, host, port, connectorName, asJson(connectorSpec, desiredConfig)) - .compose(ignored -> apiClient.statusWithBackOff(reconciliation, new BackOff(200L, 2, 10), host, port, - connectorName)) + return VertxUtil.completableFutureToVertxFuture(apiClient.createOrUpdatePutRequest(reconciliation, host, port, connectorName, asJson(connectorSpec, desiredConfig))) + .compose(ignored -> VertxUtil.completableFutureToVertxFuture(apiClient.statusWithBackOff(reconciliation, new BackOff(200L, 2, 10), host, port, + connectorName))) .compose(status -> updateState(reconciliation, host, apiClient, connectorName, connectorSpec, status, new ArrayList<>())) - .compose(ignored -> apiClient.status(reconciliation, host, port, connectorName)); + .compose(ignored -> VertxUtil.completableFutureToVertxFuture(apiClient.status(reconciliation, host, port, connectorName))); } private Future> updateState(Reconciliation reconciliation, String host, KafkaConnectApi apiClient, String connectorName, KafkaConnectorSpec connectorSpec, Map status, List conditions) { @@ -469,28 +469,28 @@ private Future> updateState(Reconciliation reconciliation, Strin case "RUNNING" -> { if (targetState == ConnectorState.PAUSED) { LOGGER.infoCr(reconciliation, "Pausing connector {}", connectorName); - future = apiClient.pause(reconciliation, host, port, connectorName); + future = VertxUtil.completableFutureToVertxFuture(apiClient.pause(reconciliation, host, port, connectorName)); } else if (targetState == ConnectorState.STOPPED) { LOGGER.infoCr(reconciliation, "Stopping connector {}", connectorName); - future = apiClient.stop(reconciliation, host, port, connectorName); + future = VertxUtil.completableFutureToVertxFuture(apiClient.stop(reconciliation, host, port, connectorName)); } } case "PAUSED" -> { if (targetState == ConnectorState.RUNNING) { LOGGER.infoCr(reconciliation, "Resuming connector {}", connectorName); - future = apiClient.resume(reconciliation, host, port, connectorName); + future = VertxUtil.completableFutureToVertxFuture(apiClient.resume(reconciliation, host, port, connectorName)); } else if (targetState == ConnectorState.STOPPED) { LOGGER.infoCr(reconciliation, "Stopping connector {}", connectorName); - future = apiClient.stop(reconciliation, host, port, connectorName); + future = VertxUtil.completableFutureToVertxFuture(apiClient.stop(reconciliation, host, port, connectorName)); } } case "STOPPED" -> { if (targetState == ConnectorState.RUNNING) { LOGGER.infoCr(reconciliation, "Resuming connector {}", connectorName); - future = apiClient.resume(reconciliation, host, port, connectorName); + future = VertxUtil.completableFutureToVertxFuture(apiClient.resume(reconciliation, host, port, connectorName)); } else if (targetState == ConnectorState.PAUSED) { LOGGER.infoCr(reconciliation, "Pausing connector {}", connectorName); - future = apiClient.pause(reconciliation, host, port, connectorName); + future = VertxUtil.completableFutureToVertxFuture(apiClient.pause(reconciliation, host, port, connectorName)); } } default -> { @@ -575,7 +575,7 @@ private Future> updateState(Reconciliation reconciliation, Strin */ private Future autoRestartConnector(Reconciliation reconciliation, String host, KafkaConnectApi apiClient, String connectorName, ConnectorStatusAndConditions status, AutoRestartStatus previousAutoRestartStatus) { LOGGER.infoCr(reconciliation, "Auto restarting connector {}", connectorName); - return apiClient.restart(host, port, connectorName, true, true) + return VertxUtil.completableFutureToVertxFuture(apiClient.restart(host, port, connectorName, true, true)) .compose( statusResult -> { LOGGER.infoCr(reconciliation, "Restarted connector {} ", connectorName); @@ -655,7 +655,7 @@ private static int nextAutoRestartBackOffIntervalInMinutes(int restartCount) private Future> maybeRestartConnector(Reconciliation reconciliation, String host, KafkaConnectApi apiClient, String connectorName, CustomResource resource, List conditions) { if (hasRestartAnnotation(resource, connectorName)) { LOGGER.debugCr(reconciliation, "Restarting connector {}", connectorName); - return apiClient.restart(host, port, connectorName, false, false) + return VertxUtil.completableFutureToVertxFuture(apiClient.restart(host, port, connectorName, false, false)) .compose(ignored -> removeRestartAnnotation(reconciliation, resource) .compose(v -> Future.succeededFuture(conditions)), throwable -> { @@ -675,7 +675,7 @@ private Future> maybeRestartConnectorTask(Reconciliation reconci int taskID = getRestartTaskAnnotationTaskID(resource, connectorName); if (taskID >= 0) { LOGGER.debugCr(reconciliation, "Restarting connector task {}:{}", connectorName, taskID); - return apiClient.restartTask(host, port, connectorName, taskID) + return VertxUtil.completableFutureToVertxFuture(apiClient.restartTask(host, port, connectorName, taskID)) .compose(ignored -> removeRestartTaskAnnotation(reconciliation, resource) .compose(v -> Future.succeededFuture(conditions)), throwable -> { @@ -761,7 +761,7 @@ private Future> listConnectorOffsets(Reconciliation reconciliati } String configMapName = listOffsetsConfig.get().getToConfigMap().getName(); - return apiClient.getConnectorOffsets(reconciliation, host, port, connectorName) + return VertxUtil.completableFutureToVertxFuture(apiClient.getConnectorOffsets(reconciliation, host, port, connectorName)) .compose(offsets -> generateListOffsetsConfigMap(configMapName, connectorName, resource, offsets)) .compose(configMap -> configMapOperations.reconcile(reconciliation, resource.getMetadata().getNamespace(), configMapName, configMap)) .compose(v -> removeConnectorOffsetsAnnotations(reconciliation, resource)) @@ -853,7 +853,7 @@ private Future> alterConnectorOffsets(Reconciliation reconciliat String configMapName = alterOffsetsConfig.get().getFromConfigMap().getName(); return verifyConnectorStopped(reconciliation, host, apiClient, connectorName) .compose(v -> getOffsetsForAlterRequest(configMapNamespace, configMapName, getConnectorOffsetsConfigMapEntryKey(connectorName))) - .compose(offsets -> apiClient.alterConnectorOffsets(reconciliation, host, port, connectorName, offsets)) + .compose(offsets -> VertxUtil.completableFutureToVertxFuture(apiClient.alterConnectorOffsets(reconciliation, host, port, connectorName, offsets))) .compose(v -> { LOGGER.infoCr(reconciliation, "Offsets of connector {} were altered", connectorName); return Future.succeededFuture(); @@ -917,7 +917,7 @@ private Future> resetConnectorOffsets(Reconciliation reconciliat LOGGER.infoCr(reconciliation, "Resetting offsets of connector {}", connectorName); return verifyConnectorStopped(reconciliation, host, apiClient, connectorName) - .compose(v -> apiClient.resetConnectorOffsets(reconciliation, host, port, connectorName)) + .compose(v -> VertxUtil.completableFutureToVertxFuture(apiClient.resetConnectorOffsets(reconciliation, host, port, connectorName))) .compose(v -> { LOGGER.infoCr(reconciliation, "Offsets of connector {} were reset", connectorName); return Future.succeededFuture(); @@ -934,7 +934,7 @@ private Future> resetConnectorOffsets(Reconciliation reconciliat } private Future verifyConnectorStopped(Reconciliation reconciliation, String host, KafkaConnectApi apiClient, String connectorName) { - return apiClient.status(reconciliation, host, port, connectorName) + return VertxUtil.completableFutureToVertxFuture(apiClient.status(reconciliation, host, port, connectorName)) .compose(status -> { @SuppressWarnings({ "rawtypes" }) Object path = ((Map) status.getOrDefault("connector", emptyMap())).get("state"); @@ -950,7 +950,7 @@ private Future verifyConnectorStopped(Reconciliation reconciliation, Strin } private Future updateConnectorTopics(Reconciliation reconciliation, String host, KafkaConnectApi apiClient, String connectorName, ConnectorStatusAndConditions status) { - return apiClient.getConnectorTopics(reconciliation, host, port, connectorName) + return VertxUtil.completableFutureToVertxFuture(apiClient.getConnectorTopics(reconciliation, host, port, connectorName)) .compose(updateConnectorStatusAndConditions(status)); } diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApi.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApi.java index 51a0d610541..70493d7a253 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApi.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApi.java @@ -8,13 +8,13 @@ import io.strimzi.operator.common.BackOff; import io.strimzi.operator.common.Reconciliation; import io.strimzi.operator.common.model.OrderedProperties; -import io.vertx.core.Future; -import io.vertx.core.http.HttpClientResponse; import io.vertx.core.json.JsonObject; +import java.net.http.HttpResponse; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; /** * A Java client for the Kafka Connect REST API. @@ -30,7 +30,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns information about the connector, including its name, config and tasks. */ - Future> createOrUpdatePutRequest(Reconciliation reconciliation, String host, int port, String connectorName, JsonObject configJson); + CompletableFuture> createOrUpdatePutRequest(Reconciliation reconciliation, String host, int port, String connectorName, JsonObject configJson); /** * Make a {@code GET} request to {@code /connectors/${connectorName}/config}. @@ -41,7 +41,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the connector's config. */ - Future> getConnectorConfig(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture> getConnectorConfig(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code GET} request to {@code /connectors/${connectorName}/config}. @@ -54,7 +54,7 @@ public interface KafkaConnectApi { * * @return A Future which completes with the result of the request. If the request was successful, this returns the connector's config. */ - Future> getConnectorConfig(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName); + CompletableFuture> getConnectorConfig(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName); /** * Make a {@code GET} request to {@code /connectors/${connectorName}}. @@ -65,7 +65,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns information about the connector, including its name, config and tasks. */ - Future> getConnector(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture> getConnector(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code DELETE} request to {@code /connectors/${connectorName}}. @@ -75,7 +75,7 @@ public interface KafkaConnectApi { * @param connectorName The name of the connector to delete. * @return A Future which completes with the result of the request. */ - Future delete(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture delete(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code GET} request to {@code /connectors/${connectorName}/status}. @@ -86,7 +86,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the connector's status. */ - Future> status(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture> status(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code GET} request to {@code /connectors/${connectorName}/status}. @@ -98,7 +98,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the connector's status. */ - Future> status(Reconciliation reconciliation, String host, int port, String connectorName, Set okStatusCodes); + CompletableFuture> status(Reconciliation reconciliation, String host, int port, String connectorName, Set okStatusCodes); /** * Make a {@code GET} request to {@code /connectors/${connectorName}/status}, retrying according to {@code backoff}. @@ -110,7 +110,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the connector's status. */ - Future> statusWithBackOff(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName); + CompletableFuture> statusWithBackOff(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName); /** * Make a {@code PUT} request to {@code /connectors/${connectorName}/pause}. @@ -120,7 +120,7 @@ public interface KafkaConnectApi { * @param connectorName The name of the connector to pause. * @return A Future which completes with the result of the request. */ - Future pause(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture pause(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code PUT} request to {@code /connectors/${connectorName}/stop}. @@ -130,7 +130,7 @@ public interface KafkaConnectApi { * @param connectorName The name of the connector to pause. * @return A Future which completes with the result of the request. */ - Future stop(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture stop(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code PUT} request to {@code /connectors/${connectorName}/resume}. @@ -140,7 +140,7 @@ public interface KafkaConnectApi { * @param connectorName The name of the connector to resume. * @return A Future which completes with the result of the request. */ - Future resume(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture resume(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code GET} request to {@code /connectors} @@ -150,7 +150,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the list of connectors. */ - Future> list(Reconciliation reconciliation, String host, int port); + CompletableFuture> list(Reconciliation reconciliation, String host, int port); /** * Make a {@code GET} request to {@code /connector-plugins}. @@ -160,7 +160,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the list of connector plugins. */ - Future> listConnectorPlugins(Reconciliation reconciliation, String host, int port); + CompletableFuture> listConnectorPlugins(Reconciliation reconciliation, String host, int port); /** * Make a {@code GET} request to {@code /admin/loggers/$logger}. @@ -172,7 +172,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns whether any loggers were actually changed. */ - Future updateConnectLoggers(Reconciliation reconciliation, String host, int port, String desiredLogging, OrderedProperties defaultLogging); + CompletableFuture updateConnectLoggers(Reconciliation reconciliation, String host, int port, String desiredLogging, OrderedProperties defaultLogging); /** * Make a {@code GET} request to {@code /admin/loggers}. @@ -182,7 +182,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the list of connect loggers. */ - Future> listConnectLoggers(Reconciliation reconciliation, String host, int port); + CompletableFuture> listConnectLoggers(Reconciliation reconciliation, String host, int port); /** * Make a {@code POST} request to {@code /connectors/${connectorName}/restart}. @@ -194,7 +194,7 @@ public interface KafkaConnectApi { * @param onlyFailed Specifies whether to restart just the instances with a FAILED status or all instances. * @return A Future which completes with the result of the request and the new status of the connector */ - Future> restart(String host, int port, String connectorName, boolean includeTasks, boolean onlyFailed); + CompletableFuture> restart(String host, int port, String connectorName, boolean includeTasks, boolean onlyFailed); /** * Make a {@code POST} request to {@code /connectors/${connectorName}/tasks/${taskID}/restart}. @@ -204,7 +204,7 @@ public interface KafkaConnectApi { * @param taskID The ID of the connector task to restart. * @return A Future which completes with the result of the request. */ - Future restartTask(String host, int port, String connectorName, int taskID); + CompletableFuture restartTask(String host, int port, String connectorName, int taskID); /** * Make a {@code GET} request to {@code /connectors/${connectorName}/topics}. @@ -215,7 +215,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the connector's topics. */ - Future> getConnectorTopics(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture> getConnectorTopics(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code GET} request to {@code /connectors/${connectorName}/offsets}. @@ -226,7 +226,7 @@ public interface KafkaConnectApi { * @return A Future which completes with the result of the request. If the request was successful, * this returns the connector's offsets. */ - Future getConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture getConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName); /** * Make a {@code PATCH} request to {@code /connectors/${connectorName}/offsets}. @@ -237,7 +237,7 @@ public interface KafkaConnectApi { * @param newOffsets The new offsets for the connector. * @return A Future which completes with the result of the request. */ - Future alterConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName, String newOffsets); + CompletableFuture alterConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName, String newOffsets); /** * Make a {@code DELETE} request to {@code /connectors/${connectorName}/offsets}. @@ -247,7 +247,7 @@ public interface KafkaConnectApi { * @param connectorName The name of the connector to reset the offsets of. * @return A Future which completes with the result of the request. */ - Future resetConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName); + CompletableFuture resetConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName); } class ConnectRestException extends RuntimeException { @@ -258,8 +258,8 @@ class ConnectRestException extends RuntimeException { this.statusCode = statusCode; } - public ConnectRestException(HttpClientResponse response, String message) { - this(response.request().getMethod().toString(), response.request().path(), response.statusCode(), response.statusMessage(), message); + public ConnectRestException(HttpResponse response, String message) { + this(response.request().method(), response.uri().getPath(), response.statusCode(), String.valueOf(response.statusCode()), message); } ConnectRestException(String method, String path, int statusCode, String statusMessage, String message, Throwable cause) { @@ -267,8 +267,8 @@ public ConnectRestException(HttpClientResponse response, String message) { this.statusCode = statusCode; } - public ConnectRestException(HttpClientResponse response, String message, Throwable cause) { - this(response.request().getMethod().toString(), response.request().path(), response.statusCode(), response.statusMessage(), message, cause); + public ConnectRestException(HttpResponse response, String message, Throwable cause) { + this(response.request().method(), response.uri().getPath(), response.statusCode(), String.valueOf(response.statusCode()), message, cause); } public int getStatusCode() { diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImpl.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImpl.java index 2510dc23d51..376fddd5711 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImpl.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImpl.java @@ -5,27 +5,27 @@ package io.strimzi.operator.cluster.operator.assembly; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import io.strimzi.api.kafka.model.connect.ConnectorPlugin; -import io.strimzi.operator.cluster.operator.resource.HttpClientUtils; import io.strimzi.operator.common.BackOff; import io.strimzi.operator.common.Reconciliation; import io.strimzi.operator.common.ReconciliationLogger; import io.strimzi.operator.common.Util; import io.strimzi.operator.common.model.OrderedProperties; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.json.DecodeException; -import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -36,6 +36,10 @@ import java.util.Optional; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -44,66 +48,54 @@ class KafkaConnectApiImpl implements KafkaConnectApi { private static final ReconciliationLogger LOGGER = ReconciliationLogger.create(KafkaConnectApiImpl.class); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static final TypeReference> TREE_TYPE = new TypeReference<>() { }; public static final TypeReference> MAP_OF_STRINGS = new TypeReference<>() { }; public static final TypeReference>> MAP_OF_MAP_OF_STRINGS = new TypeReference<>() { }; public static final TypeReference>>> MAP_OF_MAP_OF_LIST_OF_STRING = new TypeReference<>() { }; - private final ObjectMapper mapper = new ObjectMapper(); - private final Vertx vertx; + private final HttpClient httpClient; - public KafkaConnectApiImpl(Vertx vertx) { - this.vertx = vertx; + public KafkaConnectApiImpl() { + this.httpClient = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); } @Override @SuppressWarnings("unchecked") - public Future> createOrUpdatePutRequest( + public CompletableFuture> createOrUpdatePutRequest( Reconciliation reconciliation, String host, int port, String connectorName, JsonObject configJson) { - Buffer data = configJson.toBuffer(); + String data = configJson.toString(); String path = "/connectors/" + connectorName + "/config"; LOGGER.debugCr(reconciliation, "Making PUT request to {} with body {}", path, configJson); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.PUT, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json") - .putHeader("Content-Type", "application/json") - .putHeader("Content-Length", String.valueOf(data.length())) - .write(data); - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200 || response.result().statusCode() == 201) { - response.result().bodyHandler(buffer -> { - try { - @SuppressWarnings({ "rawtypes" }) - Map t = mapper.readValue(buffer.getBytes(), Map.class); - LOGGER.debugCr(reconciliation, "Got {} response to PUT request to {}: {}", response.result().statusCode(), path, t); - result.complete(t); - } catch (IOException e) { - result.fail(new ConnectRestException(response.result(), "Could not deserialize response: " + e)); - } - }); - } else { - // TODO Handle 409 (Conflict) indicating a rebalance in progress - LOGGER.debugCr(reconciliation, "Got {} response to PUT request to {}", response.result().statusCode(), path); - response.result().bodyHandler(buffer -> { - result.fail(new ConnectRestException(response.result(), tryToExtractErrorMessage(reconciliation, buffer))); - }); - } - } else { - result.tryFail(response.cause()); - } - }); - } else { - result.fail(request.cause()); - } - })); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .PUT(HttpRequest.BodyPublishers.ofString(data)) + .setHeader("Accept", "application/json") + .setHeader("Content-Type", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 200 || statusCode == 201) { + JsonNode json = parseToJsonNode(response); + Map t = OBJECT_MAPPER.convertValue(json, TREE_TYPE); + LOGGER.debugCr(reconciliation, "Got {} response to PUT request to {}: {}", statusCode, path, t); + return CompletableFuture.completedFuture(t); + } else { + // TODO Handle 409 (Conflict) indicating a rebalance in progress (https://github.com/strimzi/strimzi-kafka-operator/issues/10933) + LOGGER.debugCr(reconciliation, "Got {} response to PUT request to {}", statusCode, path); + return CompletableFuture.failedFuture(new ConnectRestException(response, tryToExtractErrorMessage(reconciliation, response.body()))); + } + }); } @Override - public Future> getConnector( + public CompletableFuture> getConnector( Reconciliation reconciliation, String host, int port, String connectorName) { @@ -112,44 +104,48 @@ public Future> getConnector( TREE_TYPE); } - private Future doGet(Reconciliation reconciliation, String host, int port, String path, Set okStatusCodes, TypeReference type) { + private JsonNode parseToJsonNode(HttpResponse responseBody) { + JsonNode json; + try { + json = OBJECT_MAPPER.readTree(responseBody.body()); + } catch (JsonProcessingException e) { + throw new ConnectRestException(responseBody, "Could not deserialize response: " + e); + } + return json; + } + + // Encodes the invalid characters for a URL because the connector name may contain them + private String encodeURLString(String toEncode) { + return toEncode.replace("->", "%2D%3E"); + } + + + private CompletableFuture doGet(Reconciliation reconciliation, String host, int port, String path, Set okStatusCodes, TypeReference type) { LOGGER.debugCr(reconciliation, "Making GET request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.GET, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - request.result().send(response -> { - if (response.succeeded()) { - if (okStatusCodes.contains(response.result().statusCode())) { - response.result().bodyHandler(buffer -> { - try { - T t = mapper.readValue(buffer.getBytes(), type); - LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}: {}", response.result().statusCode(), path, t); - result.complete(t); - } catch (IOException e) { - result.fail(new ConnectRestException(response.result(), "Could not deserialize response: " + e)); - } - }); - } else { - // TODO Handle 409 (Conflict) indicating a rebalance in progress - LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}", response.result().statusCode(), path); - response.result().bodyHandler(buffer -> { - result.fail(new ConnectRestException(response.result(), tryToExtractErrorMessage(reconciliation, buffer))); - }); - } - } else { - result.tryFail(response.cause()); - } - }); - } else { - result.tryFail(request.cause()); - } - })); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .GET() + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (okStatusCodes.contains(statusCode)) { + JsonNode json = parseToJsonNode(response); + LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}: {}", statusCode, path, json.asText()); + return CompletableFuture.completedFuture(OBJECT_MAPPER.convertValue(json, type)); + } else { + // TODO Handle 409 (Conflict) indicating a rebalance in progress (https://github.com/strimzi/strimzi-kafka-operator/issues/10933) + LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}", statusCode, path); + return CompletableFuture.failedFuture(new ConnectRestException(response, tryToExtractErrorMessage(reconciliation, response.body()))); + } + }); } @Override - public Future> getConnectorConfig( + public CompletableFuture> getConnectorConfig( Reconciliation reconciliation, String host, int port, String connectorName) { @@ -159,315 +155,269 @@ public Future> getConnectorConfig( } @Override - public Future> getConnectorConfig(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName) { + public CompletableFuture> getConnectorConfig(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName) { return withBackoff(reconciliation, backOff, connectorName, Collections.singleton(409), () -> getConnectorConfig(reconciliation, host, port, connectorName), "config"); } @Override - public Future delete(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture delete(Reconciliation reconciliation, String host, int port, String connectorName) { String path = "/connectors/" + connectorName; LOGGER.debugCr(reconciliation, "Making DELETE request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.DELETE, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json") - .putHeader("Content-Type", "application/json"); - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 204) { - LOGGER.debugCr(reconciliation, "Connector was deleted. Waiting for status deletion!"); - withBackoff(reconciliation, new BackOff(200L, 2, 10), connectorName, Collections.singleton(200), - () -> status(reconciliation, host, port, connectorName, Collections.singleton(404)), "status") - .onComplete(res -> { - if (res.succeeded()) { - result.complete(); - } else { - result.fail(res.cause()); - } - }); - } else { - // TODO Handle 409 (Conflict) indicating a rebalance in progress - LOGGER.debugCr(reconciliation, "Got {} response to DELETE request to {}", response.result().statusCode(), path); - response.result().bodyHandler(buffer -> { - result.fail(new ConnectRestException(response.result(), tryToExtractErrorMessage(reconciliation, buffer))); - }); - } - } else { - result.tryFail(response.cause()); - } - }); - } else { - result.tryFail(request.cause()); - } - })); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .DELETE() + .setHeader("Accept", "application/json") + .setHeader("Content-Type", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 204) { + LOGGER.debugCr(reconciliation, "Connector was deleted. Waiting for status deletion!"); + return withBackoff(reconciliation, new BackOff(200L, 2, 10), connectorName, Collections.singleton(200), + () -> status(reconciliation, host, port, connectorName, Collections.singleton(404)), "status").thenApply(r -> null); + } else { + // TODO Handle 409 (Conflict) indicating a rebalance in progress (https://github.com/strimzi/strimzi-kafka-operator/issues/10933) + LOGGER.debugCr(reconciliation, "Got {} response to PUT request to {}", statusCode, path); + return CompletableFuture.failedFuture(new ConnectRestException(response, tryToExtractErrorMessage(reconciliation, response.body()))); + } + }); } @Override - public Future> statusWithBackOff(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName) { + public CompletableFuture> statusWithBackOff(Reconciliation reconciliation, BackOff backOff, String host, int port, String connectorName) { return withBackoff(reconciliation, backOff, connectorName, Collections.singleton(404), () -> status(reconciliation, host, port, connectorName), "status"); } - private Future withBackoff(Reconciliation reconciliation, - BackOff backOff, String connectorName, - Set retriableStatusCodes, - Supplier> supplier, - String attribute) { - Promise result = Promise.promise(); - - Handler handler = new Handler() { - @Override - public void handle(Long tid) { - supplier.get().onComplete(connectorStatus -> { - if (connectorStatus.succeeded()) { - result.complete(connectorStatus.result()); - } else { - Throwable cause = connectorStatus.cause(); - if (cause instanceof ConnectRestException - && retriableStatusCodes.contains(((ConnectRestException) cause).getStatusCode())) { - if (backOff.done()) { - LOGGER.debugCr(reconciliation, "Connector {} {} returned HTTP {} and we run out of back off time", connectorName, attribute, ((ConnectRestException) cause).getStatusCode()); - result.fail(cause); - } else { - LOGGER.debugCr(reconciliation, "Connector {} {} returned HTTP {} - backing off", connectorName, attribute, ((ConnectRestException) cause).getStatusCode()); - rescheduleOrComplete(tid); - } + private CompletableFuture withBackoff(Reconciliation reconciliation, + BackOff backOff, String connectorName, + Set retriableStatusCodes, + Supplier> supplier, + String attribute) { + CompletableFuture statusFuture = new CompletableFuture<>(); + ScheduledExecutorService singleExecutor = Executors.newSingleThreadScheduledExecutor( + runnable -> new Thread(runnable, "kafka-connect-" + connectorName + "-" + attribute)); + + executeBackOffInternal(reconciliation, backOff, connectorName, retriableStatusCodes, singleExecutor, statusFuture, supplier, attribute, 0); + statusFuture.whenComplete((r, e) -> singleExecutor.shutdown()); + return statusFuture; + } + + private void executeBackOffInternal(Reconciliation reconciliation, + BackOff backOff, String connectorName, + Set retriableStatusCodes, + ScheduledExecutorService singleExecutor, + CompletableFuture statusFuture, + Supplier> supplier, + String attribute, long delay) { + singleExecutor.schedule(() -> { + supplier.get().whenComplete((result, error) -> { + if (error == null) { + statusFuture.complete(result); + } else { + Throwable cause = error.getCause(); + if (cause instanceof ConnectRestException + && retriableStatusCodes.contains(((ConnectRestException) cause).getStatusCode())) { + if (backOff.done()) { + LOGGER.debugCr(reconciliation, "Connector {} {} returned HTTP {} and we run out of back off time", connectorName, attribute, ((ConnectRestException) cause).getStatusCode()); + statusFuture.completeExceptionally(cause); } else { - result.fail(cause); - } - } - }); - } + LOGGER.debugCr(reconciliation, "Connector {} {} returned HTTP {} - backing off", connectorName, attribute, ((ConnectRestException) cause).getStatusCode()); + long delay1 = backOff.delayMs(); - void rescheduleOrComplete(Long tid) { - if (backOff.done()) { - LOGGER.warnCr(reconciliation, "Giving up waiting for status of connector {} after {} attempts taking {}ms", - connectorName, backOff.maxAttempts(), backOff.totalDelayMs()); - } else { - // Schedule ourselves to run again - long delay = backOff.delayMs(); - LOGGER.debugCr(reconciliation, "Status for connector {} not found; " + - "backing off for {}ms (cumulative {}ms)", - connectorName, delay, backOff.cumulativeDelayMs()); - if (delay < 1) { - this.handle(tid); + LOGGER.debugCr(reconciliation, "Status for connector {} not found; " + + "backing off for {}ms (cumulative {}ms)", + connectorName, delay, backOff.cumulativeDelayMs()); + executeBackOffInternal(reconciliation, backOff, connectorName, retriableStatusCodes, singleExecutor, statusFuture, supplier, attribute, delay1); + } } else { - vertx.setTimer(delay, this); + statusFuture.completeExceptionally(cause); } } - } - }; - - handler.handle(null); - return result.future(); + }); + }, delay, TimeUnit.MILLISECONDS); } @Override - public Future> status(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture> status(Reconciliation reconciliation, String host, int port, String connectorName) { return status(reconciliation, host, port, connectorName, Collections.singleton(200)); } @Override - public Future> status(Reconciliation reconciliation, String host, int port, String connectorName, Set okStatusCodes) { + public CompletableFuture> status(Reconciliation reconciliation, String host, int port, String connectorName, Set okStatusCodes) { String path = "/connectors/" + connectorName + "/status"; return doGet(reconciliation, host, port, path, okStatusCodes, TREE_TYPE); } @Override - public Future pause(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture pause(Reconciliation reconciliation, String host, int port, String connectorName) { return updateState(reconciliation, host, port, "/connectors/" + connectorName + "/pause", 202); } @Override - public Future stop(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture stop(Reconciliation reconciliation, String host, int port, String connectorName) { return updateState(reconciliation, host, port, "/connectors/" + connectorName + "/stop", 204); } @Override - public Future resume(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture resume(Reconciliation reconciliation, String host, int port, String connectorName) { return updateState(reconciliation, host, port, "/connectors/" + connectorName + "/resume", 202); } - private Future updateState(Reconciliation reconciliation, String host, int port, String path, int expectedStatusCode) { + private CompletableFuture updateState(Reconciliation reconciliation, String host, int port, String path, int expectedStatusCode) { LOGGER.debugCr(reconciliation, "Making PUT request to {} ", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.PUT, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == expectedStatusCode) { - response.result().bodyHandler(body -> { - result.complete(); - }); - } else { - result.fail("Unexpected status code " + response.result().statusCode() - + " for PUT request to " + host + ":" + port + path); - } - } else { - result.tryFail(response.cause()); - } - }); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .PUT(HttpRequest.BodyPublishers.noBody()) + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + if (response.statusCode() == expectedStatusCode) { + return CompletableFuture.completedFuture(null); } else { - result.tryFail(request.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); } - })); + }); } @Override - public Future> list(Reconciliation reconciliation, String host, int port) { + public CompletableFuture> list(Reconciliation reconciliation, String host, int port) { String path = "/connectors"; LOGGER.debugCr(reconciliation, "Making GET request to {} ", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.GET, port, host, path, request -> { - - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200) { - response.result().bodyHandler(buffer -> { - JsonArray objects = buffer.toJsonArray(); - List list = new ArrayList<>(objects.size()); - for (Object o : objects) { - if (o instanceof String) { - list.add((String) o); - } else { - result.fail(o == null ? "null" : o.getClass().getName()); - } - } - result.complete(list); - }); - } else { - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); - } + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, path))) + .GET() + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + if (response.statusCode() == 200) { + JsonNode json = parseToJsonNode(response); + if (!json.isArray()) { + return CompletableFuture.failedFuture(new ConnectRestException(response, "Response body is not a JSON array")); + } + + ArrayNode objects = (ArrayNode) json; + List list = new ArrayList<>(objects.size()); + + for (Object o : objects) { + if (o instanceof TextNode) { + list.add(((TextNode) o).asText()); } else { - result.tryFail(response.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, o == null ? "null" : o.getClass().getName())); } - }); + } + return CompletableFuture.completedFuture(list); } else { - result.tryFail(request.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); } - })); + }); } @Override - public Future> listConnectorPlugins(Reconciliation reconciliation, String host, int port) { + public CompletableFuture> listConnectorPlugins(Reconciliation reconciliation, String host, int port) { String path = "/connector-plugins"; LOGGER.debugCr(reconciliation, "Making GET request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.GET, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200) { - response.result().bodyHandler(buffer -> { - try { - LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}", response.result().statusCode()); - result.complete(asList(mapper.readValue(buffer.getBytes(), ConnectorPlugin[].class))); - } catch (IOException e) { - LOGGER.warnCr(reconciliation, "Failed to parse list of connector plugins", e); - result.fail(new ConnectRestException(response.result(), "Failed to parse list of connector plugins", e)); - } - }); - } else { - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); - } - } else { - result.tryFail(response.cause()); - } - }); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, path))) + .GET() + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 200) { + try { + LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}", statusCode); + return CompletableFuture.completedFuture(asList(OBJECT_MAPPER.readValue(response.body().getBytes(StandardCharsets.UTF_8), ConnectorPlugin[].class))); + } catch (IOException e) { + LOGGER.warnCr(reconciliation, "Failed to parse list of connector plugins", e); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Failed to parse list of connector plugins", e)); + } } else { - result.tryFail(request.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); } - })); + }); } - private Future updateConnectorLogger(Reconciliation reconciliation, String host, int port, String logger, String level) { + private CompletableFuture updateConnectorLogger(Reconciliation reconciliation, String host, int port, String logger, String level) { String path = "/admin/loggers/" + logger + "?scope=cluster"; - JsonObject levelJO = new JsonObject(); + + ObjectNode levelJO = OBJECT_MAPPER.createObjectNode(); levelJO.put("level", level); + String data; + try { + data = OBJECT_MAPPER.writeValueAsString(levelJO); + } catch (JsonProcessingException e) { + return CompletableFuture.failedFuture(new RuntimeException("Could not deserialize the request data for updating logger " + logger + ": " + e)); + } + LOGGER.debugCr(reconciliation, "Making PUT request to {} with body {}", path, levelJO); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> { - Buffer buffer = levelJO.toBuffer(); - httpClient - .request(HttpMethod.PUT, port, host, path, request -> { - if (request.succeeded()) { - request.result().putHeader("Content-Type", "application/json") - .setFollowRedirects(true) - .putHeader("Content-Length", Integer.toString(buffer.toString().length())) - .write(buffer.toString()); - request.result().send(response -> { - if (response.succeeded()) { - if (List.of(200, 204).contains(response.result().statusCode())) { - response.result().bodyHandler(body -> { - LOGGER.debugCr(reconciliation, "Logger {} updated to level {}", logger, level); - result.complete(); - }); - } else { - LOGGER.debugCr(reconciliation, "Logger {} did not update to level {} (http code {})", logger, level, response.result().statusCode()); - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); - } - } else { - result.tryFail(response.cause()); - } - }); - } else { - result.tryFail(request.cause()); - } - }); - }); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, path))) + .PUT(HttpRequest.BodyPublishers.ofString(data)) + .setHeader("Accept", "application/json") + .setHeader("Content-Type", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (List.of(200, 204).contains(statusCode)) { + LOGGER.debugCr(reconciliation, "Logger {} updated to level {}", logger, level); + return CompletableFuture.completedFuture(null); + } else { + LOGGER.debugCr(reconciliation, "Logger {} did not update to level {} (http code {})", logger, level, statusCode); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); + } + }); } @Override - public Future> listConnectLoggers(Reconciliation reconciliation, String host, int port) { + public CompletableFuture> listConnectLoggers(Reconciliation reconciliation, String host, int port) { String path = "/admin/loggers/"; LOGGER.debugCr(reconciliation, "Making GET request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.GET, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200) { - response.result().bodyHandler(buffer -> { - try { - LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}", response.result().statusCode(), path); - Map> fetchedLoggers = mapper.readValue(buffer.getBytes(), MAP_OF_MAP_OF_STRINGS); - Map loggerMap = new HashMap<>(fetchedLoggers.size()); - for (var loggerEntry : fetchedLoggers.entrySet()) { - String level = loggerEntry.getValue().get("level"); - if (level != null) { - loggerMap.put(loggerEntry.getKey(), level); - } - } - result.tryComplete(loggerMap); - } catch (IOException e) { - LOGGER.warnCr(reconciliation, "Failed to get list of connector loggers", e); - result.fail(new ConnectRestException(response.result(), "Failed to get connector loggers", e)); - } - }); - } else { - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, path))) + .GET() + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 200) { + try { + LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}", statusCode, path); + Map> fetchedLoggers = OBJECT_MAPPER.readValue(response.body().getBytes(StandardCharsets.UTF_8), MAP_OF_MAP_OF_STRINGS); + Map loggerMap = new HashMap<>(fetchedLoggers.size()); + for (var loggerEntry : fetchedLoggers.entrySet()) { + String level = loggerEntry.getValue().get("level"); + if (level != null) { + loggerMap.put(loggerEntry.getKey(), level); } - } else { - result.tryFail(response.cause()); } - }); + return CompletableFuture.completedFuture(loggerMap); + } catch (IOException e) { + LOGGER.warnCr(reconciliation, "Failed to get list of connector loggers", e); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Failed to get connector loggers", e)); + } } else { - result.tryFail(request.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); } - })); + }); } - private Future updateLoggers(Reconciliation reconciliation, String host, int port, + private CompletableFuture updateLoggers(Reconciliation reconciliation, String host, int port, String desiredLogging, Map fetchedLoggers, OrderedProperties defaultLogging) { @@ -490,14 +440,14 @@ private Future updateLoggers(Reconciliation reconciliation, String host addToLoggers(desiredMap, updateLoggers); if (updateLoggers.equals(fetchedLoggers)) { - return Future.succeededFuture(false); + return CompletableFuture.completedFuture(false); } else { - Future result = Future.succeededFuture(); + CompletableFuture result = CompletableFuture.completedFuture(null); for (Map.Entry logger : updateLoggers.entrySet()) { - result = result.compose(previous -> updateConnectorLogger(reconciliation, host, port, + result = result.thenCompose(previous -> updateConnectorLogger(reconciliation, host, port, logger.getKey(), logger.getValue())); } - return result.map(true); + return result.thenApply(r -> true); } } @@ -553,227 +503,178 @@ private String getLoggerLevelFromAppenderCouple(String couple) { } @Override - public Future updateConnectLoggers(Reconciliation reconciliation, String host, int port, String desiredLogging, OrderedProperties defaultLogging) { + public CompletableFuture updateConnectLoggers(Reconciliation reconciliation, String host, int port, String desiredLogging, OrderedProperties defaultLogging) { return listConnectLoggers(reconciliation, host, port) - .compose(fetchedLoggers -> updateLoggers(reconciliation, host, port, desiredLogging, fetchedLoggers, defaultLogging)); + .thenCompose(fetchedLoggers -> updateLoggers(reconciliation, host, port, desiredLogging, fetchedLoggers, defaultLogging)); } @Override - public Future> restart(String host, int port, String connectorName, boolean includeTasks, boolean onlyFailed) { + public CompletableFuture> restart(String host, int port, String connectorName, boolean includeTasks, boolean onlyFailed) { return restartConnectorOrTask(host, port, "/connectors/" + connectorName + "/restart?includeTasks=" + includeTasks + "&onlyFailed=" + onlyFailed); } @Override - public Future restartTask(String host, int port, String connectorName, int taskID) { + public CompletableFuture restartTask(String host, int port, String connectorName, int taskID) { return restartConnectorOrTask(host, port, "/connectors/" + connectorName + "/tasks/" + taskID + "/restart") - .compose(result -> Future.succeededFuture()); - } - - private Future> restartConnectorOrTask(String host, int port, String path) { - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.POST, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 202) { - response.result().bodyHandler(body -> { - try { - var status = mapper.readValue(body.getBytes(), TREE_TYPE); - result.complete(status); - } catch (IOException e) { - result.fail(new ConnectRestException(response.result(), "Failed to parse restart status response", e)); - } - }); - } else if (response.result().statusCode() == 204) { - response.result().bodyHandler(body -> { - result.complete(null); - }); - } else { - result.fail("Unexpected status code " + response.result().statusCode() - + " for POST request to " + host + ":" + port + path); - } - } else { - result.tryFail(response.cause()); + .thenCompose(result -> CompletableFuture.completedFuture(null)); + } + + private CompletableFuture> restartConnectorOrTask(String host, int port, String path) { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .POST(HttpRequest.BodyPublishers.noBody()) + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + if (response.statusCode() == 202) { + try { + return CompletableFuture.completedFuture(OBJECT_MAPPER.readValue(response.body().getBytes(StandardCharsets.UTF_8), TREE_TYPE)); + } catch (IOException e) { + return CompletableFuture.failedFuture(new ConnectRestException(response, "Failed to parse restart status response", e)); } - }); - } else { - result.tryFail(request.cause()); - } - })); + } else if (response.statusCode() == 204) { + return CompletableFuture.completedFuture(null); + } else { + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); + } + }); } @Override - public Future> getConnectorTopics(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture> getConnectorTopics(Reconciliation reconciliation, String host, int port, String connectorName) { String path = String.format("/connectors/%s/topics", connectorName); LOGGER.debugCr(reconciliation, "Making GET request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.GET, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - } else { - result.tryFail(request.cause()); - } - if (request.succeeded()) { - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200) { - response.result().bodyHandler(buffer -> { - try { - Map>> t = mapper.readValue(buffer.getBytes(), MAP_OF_MAP_OF_LIST_OF_STRING); - LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}: {}", response.result().statusCode(), path, t); - result.complete(t.get(connectorName).get("topics")); - } catch (IOException e) { - LOGGER.warnCr(reconciliation, "Failed to parse list of connector topics", e); - result.fail(new ConnectRestException(response.result(), "Failed to parse list of connector topics", e)); - } - }); - } else { - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); - } - } else { - result.fail(response.cause()); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .GET() + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 200) { + try { + Map>> t = OBJECT_MAPPER.readValue(response.body().getBytes(StandardCharsets.UTF_8), MAP_OF_MAP_OF_LIST_OF_STRING); + LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}: {}", statusCode, path, t); + return CompletableFuture.completedFuture(t.get(connectorName).get("topics")); + } catch (IOException e) { + LOGGER.warnCr(reconciliation, "Failed to parse list of connector topics", e); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Failed to parse list of connector topics", e)); } - }); - } else { - result.tryFail(request.cause()); - } - })); + } else { + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); + } + }); } @Override - public Future getConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture getConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName) { String path = String.format("/connectors/%s/offsets", connectorName); LOGGER.debugCr(reconciliation, "Making GET request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.GET, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json") - .putHeader("Content-Type", "application/json"); - } else { - result.tryFail(request.cause()); - } - if (request.succeeded()) { - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200) { - response.result().bodyHandler(buffer -> { - try { - Object offsets = mapper.readValue(buffer.getBytes(), Object.class); - String prettyPrintedOffsets = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(offsets); - LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}: {}", response.result().statusCode(), path, offsets); - result.complete(prettyPrintedOffsets); - } catch (IOException e) { - LOGGER.warnCr(reconciliation, "Failed to parse connector offsets", e); - result.fail(new ConnectRestException(response.result(), "Failed to parse connector offsets", e)); - } - }); - } else { - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); - } - } else { - result.fail(response.cause()); - } - }); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .GET() + .setHeader("Accept", "application/json") + .setHeader("Content-Type", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 200) { + try { + Object offsets = OBJECT_MAPPER.readValue(response.body().getBytes(StandardCharsets.UTF_8), Object.class); + String prettyPrintedOffsets = OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(offsets); + LOGGER.debugCr(reconciliation, "Got {} response to GET request to {}: {}", statusCode, path, offsets); + return CompletableFuture.completedFuture(prettyPrintedOffsets); + } catch (IOException e) { + LOGGER.warnCr(reconciliation, "Failed to parse connector offsets", e); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Failed to parse connector offsets", e)); + } } else { - result.tryFail(request.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); } - })); + }); } @Override - public Future alterConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName, String newOffsets) { + public CompletableFuture alterConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName, String newOffsets) { String path = String.format("/connectors/%s/offsets", connectorName); LOGGER.debugCr(reconciliation, "Making PATCH request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.PATCH, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json") - .putHeader("Content-Type", "application/json") - .putHeader("Content-Length", Integer.toString(newOffsets.length())) - .write(newOffsets); - } else { - result.tryFail(request.cause()); - } - if (request.succeeded()) { - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200) { - response.result().bodyHandler(buffer -> { - try { - Map body = mapper.readValue(buffer.getBytes(), MAP_OF_STRINGS); - String message = body.get("message"); - LOGGER.debugCr(reconciliation, "Got {} response to PATCH request to {}: {}", response.result().statusCode(), path, message); - result.complete(); - } catch (IOException e) { - LOGGER.warnCr(reconciliation, "Failed to parse connector offsets alter response", e); - result.fail(new ConnectRestException(response.result(), "Failed to parse connector offsets alter response", e)); - } - }); - } else { - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); - } - } else { - result.fail(response.cause()); - } - }); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .method("PATCH", HttpRequest.BodyPublishers.ofString(newOffsets)) + .setHeader("Accept", "application/json") + .setHeader("Content-Type", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 200) { + try { + Map body = OBJECT_MAPPER.readValue(response.body().getBytes(StandardCharsets.UTF_8), MAP_OF_STRINGS); + String message = body.get("message"); + LOGGER.debugCr(reconciliation, "Got {} response to PATCH request to {}: {}", statusCode, path, message); + return CompletableFuture.completedFuture(null); + } catch (IOException e) { + LOGGER.warnCr(reconciliation, "Failed to parse connector offsets alter response", e); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Failed to parse connector offsets alter response", e)); + } } else { - result.tryFail(request.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); } - })); + }); } @Override - public Future resetConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture resetConnectorOffsets(Reconciliation reconciliation, String host, int port, String connectorName) { String path = String.format("/connectors/%s/offsets", connectorName); LOGGER.debugCr(reconciliation, "Making DELETE request to {}", path); - return HttpClientUtils.withHttpClient(vertx, new HttpClientOptions().setLogActivity(true), (httpClient, result) -> - httpClient.request(HttpMethod.DELETE, port, host, path, request -> { - if (request.succeeded()) { - request.result().setFollowRedirects(true) - .putHeader("Accept", "application/json"); - } else { - result.tryFail(request.cause()); - } - if (request.succeeded()) { - request.result().send(response -> { - if (response.succeeded()) { - if (response.result().statusCode() == 200) { - response.result().bodyHandler(buffer -> { - try { - Map body = mapper.readValue(buffer.getBytes(), MAP_OF_STRINGS); - String message = body.get("message"); - LOGGER.debugCr(reconciliation, "Got {} response to DELETE request to {}: {}", response.result().statusCode(), path, message); - result.complete(); - } catch (IOException e) { - LOGGER.warnCr(reconciliation, "Failed to parse connector offsets reset response", e); - result.fail(new ConnectRestException(response.result(), "Failed to parse connector offsets reset response", e)); - } - }); - } else { - result.fail(new ConnectRestException(response.result(), "Unexpected status code")); - } - } else { - result.fail(response.cause()); - } - }); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(String.format("http://%s:%d%s", host, port, encodeURLString(path)))) + .DELETE() + .setHeader("Accept", "application/json") + .build(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenCompose(response -> { + int statusCode = response.statusCode(); + if (statusCode == 200) { + try { + Map body = OBJECT_MAPPER.readValue(response.body().getBytes(StandardCharsets.UTF_8), MAP_OF_STRINGS); + String message = body.get("message"); + LOGGER.debugCr(reconciliation, "Got {} response to DELETE request to {}: {}", statusCode, path, message); + return CompletableFuture.completedFuture(null); + } catch (IOException e) { + LOGGER.warnCr(reconciliation, "Failed to parse connector offsets reset response", e); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Failed to parse connector offsets reset response", e)); + } } else { - result.tryFail(request.cause()); + return CompletableFuture.failedFuture(new ConnectRestException(response, "Unexpected status code")); } - })); + }); } - /* test */ static String tryToExtractErrorMessage(Reconciliation reconciliation, Buffer buffer) { + /* test */ static String tryToExtractErrorMessage(Reconciliation reconciliation, String body) { + JsonNode json; try { - return buffer.toJsonObject().getString("message"); - } catch (DecodeException e) { - LOGGER.warnCr(reconciliation, "Failed to decode the error message from the response: " + buffer, e); + json = OBJECT_MAPPER.readTree(body); + if (json.has("message")) { + return json.get("message").asText(); + } else { + LOGGER.warnCr(reconciliation, "Failed to decode the error message from the response: " + body); + } + } catch (JsonProcessingException e) { + LOGGER.warnCr(reconciliation, "Failed to deserialize the error message from the response: " + body); } - return "Unknown error message"; } } diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java index 38db84b9393..20ed30a2db3 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java @@ -94,7 +94,7 @@ public class KafkaConnectAssemblyOperator extends AbstractConnectOperator new KafkaConnectApiImpl(vertx)); + this(vertx, pfa, supplier, config, connect -> new KafkaConnectApiImpl()); } /** @@ -336,7 +336,7 @@ private Future generateAuthHash(String namespace, KafkaConnectSpec kafk */ private Future reconcileAvailableConnectorPlugins(Reconciliation reconciliation, String host, KafkaConnectStatus connectStatus) { KafkaConnectApi apiClient = connectClientProvider.apply(vertx); - return apiClient.listConnectorPlugins(reconciliation, host, port) + return VertxUtil.completableFutureToVertxFuture(apiClient.listConnectorPlugins(reconciliation, host, port)) .compose(connectorPlugins -> { LOGGER.debugCr(reconciliation, "Setting list of connector plugins in Kafka Connect status"); connectStatus.setConnectorPlugins(connectorPlugins); @@ -370,7 +370,7 @@ private Future reconcileConnectors(Reconciliation reconciliation, KafkaCon KafkaConnectApi apiClient = connectClientProvider.apply(vertx); return Future.join( - apiClient.list(reconciliation, host, port), + VertxUtil.completableFutureToVertxFuture(apiClient.list(reconciliation, host, port)), connectorOperator.listAsync(namespace, new LabelSelectorBuilder().addToMatchLabels(Labels.STRIMZI_CLUSTER_LABEL, connectName).build()) ).compose(cf -> { List runningConnectorNames = cf.resultAt(0); @@ -479,7 +479,7 @@ private Future reconcileConnector(Reconciliation r if (connector == null) { if (useResources) { LOGGER.infoCr(reconciliation, "deleting connector: {}", connectorName); - return apiClient.delete(reconciliation, host, port, connectorName).mapEmpty(); + return VertxUtil.completableFutureToVertxFuture(apiClient.delete(reconciliation, host, port, connectorName)).mapEmpty(); } else { return Future.succeededFuture(); } diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java index afb98428249..ad93c2e9198 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java @@ -79,7 +79,7 @@ public class KafkaMirrorMaker2AssemblyOperator extends AbstractConnectOperator new KafkaConnectApiImpl(vertx)); + this(vertx, pfa, supplier, config, connect -> new KafkaConnectApiImpl()); } /** @@ -246,7 +246,7 @@ private Future generateAuthHash(String namespace, KafkaMirrorMaker2Spec KafkaConnectApi apiClient = connectClientProvider.apply(vertx); List desiredConnectors = mirrorMaker2Cluster.connectors().generateConnectorDefinitions(); - return apiClient.list(reconciliation, host, port).compose(currentConnectors -> { + return VertxUtil.completableFutureToVertxFuture(apiClient.list(reconciliation, host, port)).compose(currentConnectors -> { currentConnectors.removeAll(desiredConnectors.stream().map(c -> c.getMetadata().getName()).collect(Collectors.toSet())); Future deletionFuture = deleteConnectors(reconciliation, host, apiClient, currentConnectors); @@ -260,7 +260,7 @@ private Future deleteConnectors(Reconciliation reconciliation, String host return Future.join(connectorsForDeletion.stream() .map(connectorName -> { LOGGER.debugCr(reconciliation, "Deleting connector {}", connectorName); - return apiClient.delete(reconciliation, host, port, connectorName); + return VertxUtil.completableFutureToVertxFuture(apiClient.delete(reconciliation, host, port, connectorName)); }) .collect(Collectors.toList())) .mapEmpty(); diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/ConnectorMockTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/ConnectorMockTest.java index 1c851362403..d7b0e3ba2ca 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/ConnectorMockTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/ConnectorMockTest.java @@ -79,6 +79,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -141,7 +142,7 @@ String key(String host, String connectorName) { return host + "##" + connectorName; } - private Future> kafkaConnectApiStatusMock(String host, String connectorName) { + private CompletableFuture> kafkaConnectApiStatusMock(String host, String connectorName) { ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); Map statusNode = new HashMap<>(); statusNode.put("name", connectorName); @@ -156,7 +157,7 @@ private Future> kafkaConnectApiStatusMock(String host, Strin List> tasks = singletonList(task); statusNode.put("tasks", tasks); - return Future.succeededFuture(statusNode); + return CompletableFuture.completedFuture(statusNode); } @BeforeAll @@ -250,7 +251,7 @@ private void setupMockConnectAPI() { when(api.list(any(), any(), anyInt())).thenAnswer(i -> { String host = i.getArgument(1); String matchingKeyPrefix = host + "##"; - return Future.succeededFuture(connectors.keySet().stream() + return CompletableFuture.completedFuture(connectors.keySet().stream() .filter(s -> s.startsWith(matchingKeyPrefix)) .map(s -> s.substring(matchingKeyPrefix.length())) .collect(Collectors.toList())); @@ -261,9 +262,9 @@ private void setupMockConnectAPI() { .withType("sink") .withVersion("1.0.0") .build(); - return Future.succeededFuture(Collections.singletonList(connectorPlugin)); + return CompletableFuture.completedFuture(Collections.singletonList(connectorPlugin)); }); - when(api.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(api.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); when(api.getConnectorConfig(any(), any(), any(), anyInt(), any())).thenAnswer(invocation -> { String host = invocation.getArgument(2); String connectorName = invocation.getArgument(4); @@ -276,9 +277,9 @@ private void setupMockConnectAPI() { map.put(entry.getKey(), entry.getValue().toString()); } } - return Future.succeededFuture(map); + return CompletableFuture.completedFuture(map); } else { - return Future.failedFuture(new ConnectRestException("GET", String.format("/connectors/%s/config", connectorName), 404, "Not Found", "")); + return CompletableFuture.failedFuture(new ConnectRestException("GET", String.format("/connectors/%s/config", connectorName), 404, "Not Found", "")); } }); when(api.getConnector(any(), any(), anyInt(), any())).thenAnswer(invocation -> { @@ -286,9 +287,9 @@ private void setupMockConnectAPI() { String connectorName = invocation.getArgument(3); ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); if (connectorStatus == null) { - return Future.failedFuture(new ConnectRestException("GET", String.format("/connectors/%s", connectorName), 404, "Not Found", "")); + return CompletableFuture.failedFuture(new ConnectRestException("GET", String.format("/connectors/%s", connectorName), 404, "Not Found", "")); } - return Future.succeededFuture(Map.of( + return CompletableFuture.completedFuture(Map.of( "name", connectorName, "config", connectorStatus.config, "tasks", Map.of())); @@ -300,14 +301,14 @@ private void setupMockConnectAPI() { String connectorName = invocation.getArgument(3); JsonObject connectorConfig = invocation.getArgument(4); connectors.putIfAbsent(key(host, connectorName), new ConnectorStatus(ConnectorState.RUNNING, connectorConfig)); - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(api.delete(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> { String host = invocation.getArgument(1); LOGGER.info("###### delete " + host); String connectorName = invocation.getArgument(3); ConnectorStatus remove = connectors.remove(key(host, connectorName)); - return remove != null ? Future.succeededFuture() : Future.failedFuture("No such connector " + connectorName); + return remove != null ? CompletableFuture.completedFuture(null) : CompletableFuture.failedFuture(new ConnectRestException("DELETE", "", 404, "No such connector " + connectorName, "")); }); when(api.statusWithBackOff(any(), any(), any(), anyInt(), anyString())).thenAnswer(invocation -> { String host = invocation.getArgument(2); @@ -326,73 +327,73 @@ private void setupMockConnectAPI() { String connectorName = invocation.getArgument(3); ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); if (connectorStatus == null) { - return Future.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); + return CompletableFuture.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); } if (!ConnectorState.PAUSED.equals(connectorStatus.state)) { connectors.put(key(host, connectorName), new ConnectorStatus(ConnectorState.PAUSED, connectorStatus.config)); } - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(api.stop(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> { String host = invocation.getArgument(1); String connectorName = invocation.getArgument(3); ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); if (connectorStatus == null) { - return Future.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); + return CompletableFuture.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); } if (!ConnectorState.STOPPED.equals(connectorStatus.state)) { connectors.put(key(host, connectorName), new ConnectorStatus(ConnectorState.STOPPED, connectorStatus.config)); } - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(api.resume(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> { String host = invocation.getArgument(1); String connectorName = invocation.getArgument(3); ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); if (connectorStatus == null) { - return Future.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); + return CompletableFuture.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); } if (ConnectorState.STOPPED.equals(connectorStatus.state) || ConnectorState.PAUSED.equals(connectorStatus.state)) { connectors.put(key(host, connectorName), new ConnectorStatus(ConnectorState.RUNNING, connectorStatus.config)); } - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(api.restart(any(), anyInt(), anyString(), anyBoolean(), anyBoolean())).thenAnswer(invocation -> { String host = invocation.getArgument(0); String connectorName = invocation.getArgument(2); ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); if (connectorStatus == null) { - return Future.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); + return CompletableFuture.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); } - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(api.restartTask(any(), anyInt(), anyString(), anyInt())).thenAnswer(invocation -> { String host = invocation.getArgument(0); String connectorName = invocation.getArgument(2); ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); if (connectorStatus == null) { - return Future.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); + return CompletableFuture.failedFuture(new ConnectRestException("PUT", "", 404, "Not found", "Connector name " + connectorName)); } - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(api.getConnectorTopics(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> { String host = invocation.getArgument(1); String connectorName = invocation.getArgument(3); ConnectorStatus connectorStatus = connectors.get(key(host, connectorName)); if (connectorStatus == null) { - return Future.failedFuture(new ConnectRestException("GET", String.format("/connectors/%s/topics", connectorName), 404, "Not Found", "")); + return CompletableFuture.failedFuture(new ConnectRestException("GET", String.format("/connectors/%s/topics", connectorName), 404, "Not Found", "")); } - return Future.succeededFuture(List.of("my-topic")); + return CompletableFuture.completedFuture(List.of("my-topic")); }); - when(api.getConnectorOffsets(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> Future.succeededFuture(connectorOffsets)); + when(api.getConnectorOffsets(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> CompletableFuture.completedFuture(connectorOffsets)); when(api.alterConnectorOffsets(any(), any(), anyInt(), anyString(), anyString())).thenAnswer(invocation -> { connectorOffsets = invocation.getArgument(4); - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(api.resetConnectorOffsets(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> { connectorOffsets = RESET_OFFSETS_JSON; - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); } @@ -1066,7 +1067,7 @@ public void testConnectorNotReadyWhenExceptionFromConnectRestApi() { String connectorName = "connector"; when(api.createOrUpdatePutRequest(any(), any(), anyInt(), anyString(), any())) - .thenAnswer(invocation -> Future.failedFuture(new ConnectRestException("GET", "/foo", 500, "Internal server error", "Bad stuff happened"))); + .thenAnswer(invocation -> CompletableFuture.failedFuture(new ConnectRestException("GET", "/foo", 500, "Internal server error", "Bad stuff happened"))); // NOTE: Clear runningConnectors as re-mocking it causes an entry to be added connectors.clear(); @@ -1519,7 +1520,7 @@ public void testConnectorRestartFail() { String connectorName = "connector"; when(api.restart(anyString(), anyInt(), anyString(), anyBoolean(), anyBoolean())) - .thenAnswer(invocation -> Future.failedFuture(new ConnectRestException("GET", "/foo", 500, "Internal server error", "Bad stuff happened"))); + .thenAnswer(invocation -> CompletableFuture.failedFuture(new ConnectRestException("GET", "/foo", 500, "Internal server error", "Bad stuff happened"))); // Create KafkaConnect cluster and wait till it's ready Crds.kafkaConnectOperation(client).inNamespace(namespace).resource(new KafkaConnectBuilder() @@ -1684,7 +1685,7 @@ public void testConnectorRestartTaskFail() { String connectorName = "connector"; when(api.restartTask(anyString(), anyInt(), anyString(), anyInt())) - .thenAnswer(invocation -> Future.failedFuture(new ConnectRestException("GET", "/foo", 500, "Internal server error", "Bad stuff happened"))); + .thenAnswer(invocation -> CompletableFuture.failedFuture(new ConnectRestException("GET", "/foo", 500, "Internal server error", "Bad stuff happened"))); // Create KafkaConnect cluster and wait till it's ready Crds.kafkaConnectOperation(client).inNamespace(namespace).resource(new KafkaConnectBuilder() @@ -1813,11 +1814,11 @@ public void testConnectScaleToZero() { eq(connectorName), any()); assertThat(connectors.keySet(), is(Collections.singleton(key("cluster-connect-api.testconnectscaletozero.svc", connectorName)))); - when(api.list(any(), any(), anyInt())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.listConnectorPlugins(any(), any(), anyInt())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.createOrUpdatePutRequest(any(), any(), anyInt(), anyString(), any())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.getConnectorConfig(any(), any(), anyInt(), any())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.getConnector(any(), any(), anyInt(), any())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.list(any(), any(), anyInt())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.listConnectorPlugins(any(), any(), anyInt())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.createOrUpdatePutRequest(any(), any(), anyInt(), anyString(), any())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.getConnectorConfig(any(), any(), anyInt(), any())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.getConnector(any(), any(), anyInt(), any())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); Crds.kafkaConnectOperation(client).inNamespace(namespace).withName(connectName).edit(spec -> new KafkaConnectBuilder(spec) .editSpec() @@ -1880,11 +1881,11 @@ public void testConnectRestAPIIssues() { eq(connectorName), any()); assertThat(connectors.keySet(), is(Collections.singleton(key("cluster-connect-api.testconnectrestapiissues.svc", connectorName)))); - when(api.list(any(), any(), anyInt())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.listConnectorPlugins(any(), any(), anyInt())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.createOrUpdatePutRequest(any(), any(), anyInt(), anyString(), any())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.getConnectorConfig(any(), any(), any(), anyInt(), any())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); - when(api.getConnector(any(), any(), anyInt(), any())).thenReturn(Future.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.list(any(), any(), anyInt())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.listConnectorPlugins(any(), any(), anyInt())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.createOrUpdatePutRequest(any(), any(), anyInt(), anyString(), any())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.getConnectorConfig(any(), any(), any(), anyInt(), any())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); + when(api.getConnector(any(), any(), anyInt(), any())).thenReturn(CompletableFuture.failedFuture(new ConnectTimeoutException("connection timed out"))); Crds.kafkaConnectOperation(client).inNamespace(namespace).withName(connectName).edit(sp -> new KafkaConnectBuilder(sp) .editSpec() @@ -1952,8 +1953,8 @@ public void testConnectorDeleteFailsOnConnectReconciliation() { String connectName = "cluster"; // this connector should be deleted on connect reconciliation - when(api.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(List.of("connector"))); - when(api.delete(any(), anyString(), anyInt(), anyString())).thenReturn(Future.failedFuture(new RuntimeException("deletion error"))); + when(api.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(List.of("connector"))); + when(api.delete(any(), anyString(), anyInt(), anyString())).thenReturn(CompletableFuture.failedFuture(new RuntimeException("deletion error"))); KafkaConnect kafkaConnect = new KafkaConnectBuilder() .withNewMetadata() diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiIT.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiIT.java index c7f784e599e..8fa02376f6c 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiIT.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiIT.java @@ -9,24 +9,21 @@ import io.strimzi.operator.common.Reconciliation; import io.strimzi.operator.common.model.OrderedProperties; import io.strimzi.test.container.StrimziKafkaCluster; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; -import io.vertx.junit5.Checkpoint; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; @@ -41,11 +38,10 @@ import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; -@ExtendWith(VertxExtension.class) public class KafkaConnectApiIT { private static StrimziKafkaCluster cluster; - private static Vertx vertx; private ConnectCluster connectCluster; private int port; @@ -69,7 +65,6 @@ public void afterEach() { @BeforeAll public static void before() throws IOException { - vertx = Vertx.vertx(); cluster = new StrimziKafkaCluster.StrimziKafkaClusterBuilder() .withKraft() .withNumberOfBrokers(1) @@ -82,17 +77,36 @@ public static void before() throws IOException { @AfterAll public static void after() { cluster.stop(); - vertx.close(); + } + + private void checkStatusWithDelay(Supplier>> statusSupplier, + ScheduledExecutorService singleExecutor, + CompletableFuture> completableFuture, + long delay) { + singleExecutor.schedule(() -> { + statusSupplier.get().whenComplete((status, error) -> { + if (error == null) { + if ("RUNNING".equals(((Map) status.getOrDefault("connector", emptyMap())).get("state"))) { + completableFuture.complete(status); + } else { + System.err.println(status); + checkStatusWithDelay(statusSupplier, singleExecutor, completableFuture, delay); + } + } else { + error.printStackTrace(); + checkStatusWithDelay(statusSupplier, singleExecutor, completableFuture, delay); + } + }); + }, delay, TimeUnit.MILLISECONDS); } @Test @SuppressWarnings({"unchecked", "checkstyle:MethodLength", "checkstyle:NPathComplexity"}) - public void test(VertxTestContext context) throws InterruptedException { - KafkaConnectApi client = new KafkaConnectApiImpl(vertx); - Checkpoint async = context.checkpoint(); + public void test() throws InterruptedException { + KafkaConnectApi client = new KafkaConnectApiImpl(); Thread.sleep(10_000L); client.listConnectorPlugins(Reconciliation.DUMMY_RECONCILIATION, "localhost", port) - .onComplete(context.succeeding(connectorPlugins -> context.verify(() -> { + .whenComplete((connectorPlugins, error) -> { assertThat(connectorPlugins.size(), greaterThanOrEqualTo(2)); ConnectorPlugin fileSink = connectorPlugins.stream() @@ -106,12 +120,12 @@ public void test(VertxTestContext context) throws InterruptedException { assertNotNull(fileSource); assertThat(fileSource.getType(), is("source")); assertThat(fileSource.getVersion(), is(not(emptyString()))); - }))) + }) - .compose(connectorPlugins -> client.list(Reconciliation.DUMMY_RECONCILIATION, "localhost", port)) - .onComplete(context.succeeding(connectorNames -> context.verify(() -> assertThat(connectorNames, is(empty()))))) + .thenCompose(connectorPlugins -> client.list(Reconciliation.DUMMY_RECONCILIATION, "localhost", port)) + .whenComplete((connectorNames, error) -> assertThat(connectorNames, is(empty()))) - .compose(connectorNames -> { + .thenCompose(connectorNames -> { JsonObject o = new JsonObject() .put("connector.class", "FileStreamSource") .put("tasks.max", "1") @@ -119,34 +133,17 @@ public void test(VertxTestContext context) throws InterruptedException { .put("topic", "my-topic"); return client.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test", o); }) - .onComplete(context.succeeding(i -> { })) - .compose(created -> { - - Promise> promise = Promise.promise(); - - Handler handler = new Handler<>() { - @Override - public void handle(Long timerId) { - client.status(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test").onComplete(result -> { - if (result.succeeded()) { - Map status = result.result(); - if ("RUNNING".equals(((Map) status.getOrDefault("connector", emptyMap())).get("state"))) { - promise.complete(status); - return; - } else { - System.err.println(status); - } - } else { - result.cause().printStackTrace(); - } - vertx.setTimer(1000, this); - }); - } - }; - vertx.setTimer(1000, handler); - return promise.future(); + + .thenCompose(created -> { + CompletableFuture> completableFuture = new CompletableFuture<>(); + ScheduledExecutorService singleExecutor = Executors.newSingleThreadScheduledExecutor( + runnable -> new Thread(runnable, "kafka-connect-api-test")); + checkStatusWithDelay(() -> client.status(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test"), singleExecutor, completableFuture, 1000); + completableFuture.whenComplete((r, e) -> singleExecutor.shutdown()); + return completableFuture; }) - .onComplete(context.succeeding(status -> context.verify(() -> { + .whenComplete((status, error) -> { + assertNull(error); assertThat(status.get("name"), is("test")); Map connectorStatus = (Map) status.getOrDefault("connector", emptyMap()); assertThat(connectorStatus.get("state"), is("RUNNING")); @@ -157,87 +154,97 @@ public void handle(Long timerId) { assertThat(an.get("state"), is("RUNNING")); assertThat(an.get("worker_id"), startsWith("localhost:")); } - }))) - .compose(status -> client.getConnectorConfig(Reconciliation.DUMMY_RECONCILIATION, new BackOff(10), "localhost", port, "test")) - .onComplete(context.succeeding(config -> context.verify(() -> { + }) + + .thenCompose(status -> client.getConnectorConfig(Reconciliation.DUMMY_RECONCILIATION, new BackOff(10), "localhost", port, "test")) + .whenComplete((config, error) -> { + assertNull(error); assertThat(config, is(Map.of("connector.class", "FileStreamSource", "file", "/dev/null", "tasks.max", "1", "name", "test", "topic", "my-topic"))); - }))) - .compose(config -> client.getConnectorConfig(Reconciliation.DUMMY_RECONCILIATION, new BackOff(10), "localhost", port, "does-not-exist")) - .onComplete(context.failing(error -> context.verify(() -> { - assertThat(error, instanceOf(ConnectRestException.class)); - assertThat(((ConnectRestException) error).getStatusCode(), is(404)); - }))) - .recover(error -> Future.succeededFuture()) + }) - .compose(ignored -> client.pause(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .onComplete(context.succeeding(i -> { })) + .thenCompose(ignored -> client.getConnectorConfig(Reconciliation.DUMMY_RECONCILIATION, new BackOff(10), "localhost", port, "does-not-exist") + .handle((r, e) -> { + assertThat(e, is(instanceOf(ConnectRestException.class))); + assertThat(((ConnectRestException) e).getStatusCode(), is(404)); + if (e == null) { + throw new RuntimeException("Expected failure when getting config for connector that doesn't exist"); + } + return null; + })) - .compose(ignored -> client.resume(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .onComplete(context.succeeding(i -> { })) + .thenCompose(ignored -> client.pause(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .compose(ignored -> client.stop(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .onComplete(context.succeeding(i -> { })) + .thenCompose(ignored -> client.resume(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .compose(ignored -> client.resume(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .onComplete(context.succeeding(i -> { })) + .thenCompose(ignored -> client.stop(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .compose(ignored -> client.restart("localhost", port, "test", true, true)) - .onComplete(context.succeeding(i -> { })) + .thenCompose(ignored -> client.resume(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .compose(ignored -> client.restartTask("localhost", port, "test", 0)) - .onComplete(context.succeeding(i -> { })) + .thenCompose(ignored -> client.restart("localhost", port, "test", true, true)) - .compose(ignored -> { + .thenCompose(ignored -> client.restartTask("localhost", port, "test", 0)) + + .thenCompose(ignored -> { JsonObject o = new JsonObject() .put("connector.class", "ThisConnectorDoesNotExist") .put("tasks.max", "1") .put("file", "/dev/null") .put("topic", "my-topic"); - return client.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "broken", o); + return client.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "broken", o) + .handle((r, e) -> { + assertThat(e.getCause(), is(instanceOf(ConnectRestException.class))); + assertThat(e.getMessage(), containsString("Failed to find any class that implements Connector and which name matches ThisConnectorDoesNotExist")); + if (e == null) { + throw new RuntimeException("Expected error: Failed to find any class that implements Connector and which name matches ThisConnectorDoesNotExist"); + } + return null; + }); }) - .onComplete(context.failing(error -> context.verify(() -> { - assertThat(error, instanceOf(ConnectRestException.class)); - - assertThat(error.getMessage(), - containsString("Failed to find any class that implements Connector and which name matches ThisConnectorDoesNotExist")); - }))) - .recover(e -> Future.succeededFuture()) - .compose(ignored -> { + + .thenCompose(ignored -> { JsonObject o = new JsonObject() .put("connector.class", "FileStreamSource") .put("tasks.max", "dog") .put("file", "/dev/null") .put("topic", "my-topic"); - return client.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "broken2", o); + return client.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "broken2", o) + .handle((r, e) -> { + assertThat(e.getCause(), is(instanceOf(ConnectRestException.class))); + assertThat(e.getMessage(), containsString("Invalid value dog for configuration tasks.max: Not a number of type INT")); + if (e == null) { + throw new RuntimeException("Expected error: Invalid value dog for configuration tasks.max: Not a number of type INT"); + } + return null; + }); }) - .onComplete(context.failing(error -> context.verify(() -> { - assertThat(error, instanceOf(ConnectRestException.class)); - assertThat(error.getMessage(), - containsString("Invalid value dog for configuration tasks.max: Not a number of type INT")); - }))) - .recover(e -> Future.succeededFuture()) - .compose(createResponse -> client.list(Reconciliation.DUMMY_RECONCILIATION, "localhost", port)) - .onComplete(context.succeeding(connectorNames -> context.verify(() -> - assertThat(connectorNames, is(singletonList("test")))))) - .compose(connectorNames -> client.delete(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) - .onComplete(context.succeeding(i -> { })) - .compose(deletedConnector -> client.list(Reconciliation.DUMMY_RECONCILIATION, "localhost", port)) - .onComplete(context.succeeding(connectorNames -> assertThat(connectorNames, is(empty())))) - .compose(connectorNames -> client.delete(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "never-existed")) - .onComplete(context.failing(error -> { - assertThat(error, instanceOf(ConnectRestException.class)); - assertThat(error.getMessage(), - containsString("Connector never-existed not found")); - async.flag(); - })); + + .thenCompose(createResponse -> client.list(Reconciliation.DUMMY_RECONCILIATION, "localhost", port)) + .whenComplete((connectorNames, error) -> + assertThat(connectorNames, is(singletonList("test")))) + + .thenCompose(connectorNames -> client.delete(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "test")) + + .thenCompose(deletedConnector -> client.list(Reconciliation.DUMMY_RECONCILIATION, "localhost", port)) + .whenComplete((connectorNames, error) -> assertThat(connectorNames, is(empty()))) + + .thenCompose(connectorNames -> client.delete(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, "never-existed") + .handle((r, e) -> { + assertThat(e.getCause(), is(instanceOf(ConnectRestException.class))); + assertThat(e.getMessage(), containsString("Connector never-existed not found")); + if (e == null) { + throw new RuntimeException("Expected error: Connector never-existed not found"); + } + return null; + })) + .join(); } @Test - public void testChangeLoggers(VertxTestContext context) { + public void testChangeLoggers() { String desired = "log4j.rootLogger=TRACE, CONSOLE\n" + "log4j.logger.org.apache.zookeeper=WARN\n" + "log4j.logger.org.reflections.Reflection=INFO\n" + @@ -246,16 +253,15 @@ public void testChangeLoggers(VertxTestContext context) { "log4j.logger.foo.bar=TRACE\n" + "log4j.logger.foo.bar.quux=DEBUG"; - KafkaConnectApi client = new KafkaConnectApiImpl(vertx); - Checkpoint async = context.checkpoint(); + KafkaConnectApi client = new KafkaConnectApiImpl(); OrderedProperties ops = new OrderedProperties(); ops.addStringPairs(desired); client.updateConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, desired, ops) - .onComplete(context.succeeding(wasChanged -> context.verify(() -> assertEquals(true, wasChanged)))) - .compose(a -> client.listConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "localhost", port) - .onComplete(context.succeeding(map -> context.verify(() -> { + .whenComplete((wasChanged, error) -> assertEquals(true, wasChanged)) + .thenCompose(a -> client.listConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "localhost", port) + .whenComplete((map, error) -> { assertThat(map.get("root"), is("TRACE")); assertThat(map.get("org.apache.zookeeper"), is("WARN")); assertThat(map.get("org.reflections"), is("FATAL")); @@ -265,12 +271,11 @@ public void testChangeLoggers(VertxTestContext context) { assertThat(map.get("foo.bar"), is("TRACE")); assertThat(map.get("foo.bar.quux"), is("DEBUG")); - })))) - .compose(a -> client.updateConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, desired, ops) - .onComplete(context.succeeding(wasChanged -> context.verify(() -> { + })) + .thenCompose(b -> client.updateConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "localhost", port, desired, ops) + .whenComplete((wasChanged, error) -> { assertEquals(false, wasChanged); - async.flag(); - })))); + })).join(); } @Test @@ -285,7 +290,7 @@ public void testHierarchy() { "log4j.logger.oorg.eclipse.jetty.util=DEBUG\n" + "log4j.logger.foo.bar.quux=DEBUG"; - KafkaConnectApiImpl client = new KafkaConnectApiImpl(vertx); + KafkaConnectApiImpl client = new KafkaConnectApiImpl(); OrderedProperties ops = new OrderedProperties(); ops.addStringPairs(desired); assertEquals("TRACE", client.getEffectiveLevel("foo.bar", ops.asMap())); diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImplTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImplTest.java index 49f9f02b599..25a258bcc97 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImplTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiImplTest.java @@ -6,18 +6,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.strimzi.operator.common.Reconciliation; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpServer; +import io.strimzi.test.TestUtils; import io.vertx.core.json.JsonObject; -import io.vertx.junit5.Checkpoint; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.model.HttpRequest; +import org.mockserver.model.HttpResponse; import java.util.Map; -import java.util.concurrent.ExecutionException; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; @@ -25,97 +24,101 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockserver.model.HttpRequest.request; -@ExtendWith(VertxExtension.class) public class KafkaConnectApiImplTest { + + private static ClientAndServer server; + + @BeforeAll + public static void setupServer() { + server = new ClientAndServer(TestUtils.getFreePort()); + } + + @BeforeEach + public void resetServer() { + if (server != null && server.isRunning()) { + server.reset(); + } + } + + @AfterAll + public static void stopServer() { + if (server != null && server.isRunning()) { + server.stop(); + } + } + @Test public void testJsonDecoding() { - assertThat(KafkaConnectApiImpl.tryToExtractErrorMessage(Reconciliation.DUMMY_RECONCILIATION, Buffer.buffer("{\"message\": \"This is the error\"}")), is("This is the error")); - assertThat(KafkaConnectApiImpl.tryToExtractErrorMessage(Reconciliation.DUMMY_RECONCILIATION, Buffer.buffer("{\"message\": \"This is the error\"")), is("Unknown error message")); - assertThat(KafkaConnectApiImpl.tryToExtractErrorMessage(Reconciliation.DUMMY_RECONCILIATION, Buffer.buffer("Not a JSON")), is("Unknown error message")); + assertThat(KafkaConnectApiImpl.tryToExtractErrorMessage(Reconciliation.DUMMY_RECONCILIATION, "{\"message\": \"This is the error\"}"), is("This is the error")); + assertThat(KafkaConnectApiImpl.tryToExtractErrorMessage(Reconciliation.DUMMY_RECONCILIATION, "{\"message\": \"This is the error\""), is("Unknown error message")); + assertThat(KafkaConnectApiImpl.tryToExtractErrorMessage(Reconciliation.DUMMY_RECONCILIATION, "Not a JSON"), is("Unknown error message")); } @Test - public void testFeatureCompletionWithBadlyFormattedError(Vertx vertx, VertxTestContext context) throws ExecutionException, InterruptedException { - HttpServer server = mockApi(vertx, 500, "Some error message"); + public void testFeatureCompletionWithBadlyFormattedError() { + HttpRequest request = request().withMethod("PUT"); + server.when(request).respond(HttpResponse.response().withBody("Some error message").withStatusCode(500)); - KafkaConnectApi api = new KafkaConnectApiImpl(vertx); + KafkaConnectApi api = new KafkaConnectApiImpl(); - Checkpoint async = context.checkpoint(); - api.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.actualPort(), "my-connector", new JsonObject()) - .onComplete(context.failing(res -> context.verify(() -> { - assertThat(res.getMessage(), containsString("Unknown error message")); - - server.close(); - async.flag(); - }))); + assertThrows(Exception.class, api.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.getPort(), "my-connector", new JsonObject()) + .whenComplete((res, ex) -> assertThat(ex.getMessage(), containsString("Unknown error message")))::join); } @Test - public void testFeatureCompletionWithWellFormattedError(Vertx vertx, VertxTestContext context) throws ExecutionException, InterruptedException { - HttpServer server = mockApi(vertx, 500, "{\"message\": \"This is the error\"}"); - - KafkaConnectApi api = new KafkaConnectApiImpl(vertx); + public void testFeatureCompletionWithWellFormattedError() { + HttpRequest request = request().withMethod("PUT"); + server.when(request).respond(HttpResponse.response().withBody("{\"message\": \"This is the error\"}").withStatusCode(500)); - Checkpoint async = context.checkpoint(); - api.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.actualPort(), "my-connector", new JsonObject()) - .onComplete(context.failing(res -> context.verify(() -> { - assertThat(res.getMessage(), containsString("This is the error")); + KafkaConnectApi api = new KafkaConnectApiImpl(); - server.close(); - async.flag(); - }))); + assertThrows(Exception.class, api.createOrUpdatePutRequest(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.getPort(), "my-connector", new JsonObject()) + .whenComplete((res, ex) -> assertThat(ex.getMessage(), containsString("This is the error")))::join); } @Test - public void testListConnectLoggersWithLevel(Vertx vertx, VertxTestContext context) throws Exception { - final HttpServer server = mockApi(vertx, 200, new ObjectMapper().writeValueAsString( - Map.of( - "org.apache.kafka.connect", + public void testListConnectLoggersWithLevel() throws Exception { + HttpRequest request = request().withMethod("GET"); + server.when(request).respond(HttpResponse.response().withBody(new ObjectMapper().writeValueAsString( Map.of( - "level", "INFO" + "org.apache.kafka.connect", + Map.of( + "level", "INFO" + ) ) - ) - )); - final KafkaConnectApi api = new KafkaConnectApiImpl(vertx); - final Checkpoint async = context.checkpoint(); - api.listConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.actualPort()) - .onComplete(context.succeeding(res -> context.verify(() -> { - assertThat(res, allOf( + )).withStatusCode(200)); + + KafkaConnectApi api = new KafkaConnectApiImpl(); + + api.listConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.getPort()) + .whenComplete((res, ex) -> assertThat(res, allOf( aMapWithSize(1), hasEntry("org.apache.kafka.connect", "INFO") - )); - server.close(); - async.flag(); - }))); + ))).join(); } @Test - public void testListConnectLoggersWithLevelAndLastModified(Vertx vertx, VertxTestContext context) throws Exception { - final HttpServer server = mockApi(vertx, 200, new ObjectMapper().writeValueAsString( - Map.of( - "org.apache.kafka.connect", + public void testListConnectLoggersWithLevelAndLastModified() throws Exception { + HttpRequest request = request().withMethod("GET"); + server.when(request).respond(HttpResponse.response().withBody(new ObjectMapper().writeValueAsString( Map.of( - "level", "WARN", - "last_modified", "2020-01-01T00:00:00.000Z" + "org.apache.kafka.connect", + Map.of( + "level", "WARN", + "last_modified", "2020-01-01T00:00:00.000Z" + ) ) - ) - )); - final KafkaConnectApi api = new KafkaConnectApiImpl(vertx); - final Checkpoint async = context.checkpoint(); - api.listConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.actualPort()) - .onComplete(context.succeeding(res -> context.verify(() -> { - assertThat(res, allOf( - aMapWithSize(1), - hasEntry("org.apache.kafka.connect", "WARN") - )); - server.close(); - async.flag(); - }))); - } + )).withStatusCode(200)); - HttpServer mockApi(Vertx vertx, int status, String body) throws InterruptedException, ExecutionException { - HttpServer httpServer = vertx.createHttpServer().requestHandler(request -> request.response().setStatusCode(status).end(body)); - return httpServer.listen(0).toCompletionStage().toCompletableFuture().get(); + KafkaConnectApi api = new KafkaConnectApiImpl(); + + api.listConnectLoggers(Reconciliation.DUMMY_RECONCILIATION, "127.0.0.1", server.getPort()) + .whenComplete((res, ex) -> assertThat(res, allOf( + aMapWithSize(1), + hasEntry("org.apache.kafka.connect", "WARN"))) + ).join(); } } \ No newline at end of file diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiMockTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiMockTest.java index 59aa673e439..4f83df48cc8 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiMockTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectApiMockTest.java @@ -6,99 +6,84 @@ import io.strimzi.operator.common.BackOff; import io.strimzi.operator.common.Reconciliation; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.junit5.Checkpoint; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import java.util.Collections; import java.util.Map; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.assertThrows; -@ExtendWith(VertxExtension.class) public class KafkaConnectApiMockTest { - private static Vertx vertx; private final BackOff backOff = new BackOff(1L, 2, 3); - @BeforeAll - public static void before() { - vertx = Vertx.vertx(); - } - - @AfterAll - public static void after() { - vertx.close(); - } - @Test - public void testStatusWithBackOffSucceedingImmediately(VertxTestContext context) { - Queue>> statusResults = new ArrayBlockingQueue<>(1); - statusResults.add(Future.succeededFuture(Collections.emptyMap())); - - KafkaConnectApi api = new MockKafkaConnectApi(vertx, statusResults); - Checkpoint async = context.checkpoint(); + public void testStatusWithBackOffSucceedingImmediately() { + Queue>> statusResults = new ArrayBlockingQueue<>(1); + statusResults.add(CompletableFuture.completedFuture(Collections.emptyMap())); + KafkaConnectApi api = new MockKafkaConnectApi(statusResults); api.statusWithBackOff(Reconciliation.DUMMY_RECONCILIATION, backOff, "some-host", 8083, "some-connector") - .onComplete(context.succeeding(res -> async.flag())); + .whenComplete((r, e) -> assertThat(e, nullValue())).join(); } @Test - public void testStatusWithBackOffSucceedingEventually(VertxTestContext context) { - Queue>> statusResults = new ArrayBlockingQueue<>(3); - statusResults.add(Future.failedFuture(new ConnectRestException(null, null, 404, null, null))); - statusResults.add(Future.failedFuture(new ConnectRestException(null, null, 404, null, null))); - statusResults.add(Future.succeededFuture(Collections.emptyMap())); + public void testStatusWithBackOffSucceedingEventually() { + Queue>> statusResults = new ArrayBlockingQueue<>(3); + statusResults.add(CompletableFuture.failedFuture(new CompletionException(new ConnectRestException(null, null, 404, null, null)))); + statusResults.add(CompletableFuture.failedFuture(new CompletionException(new ConnectRestException(null, null, 404, null, null)))); + statusResults.add(CompletableFuture.completedFuture(Collections.emptyMap())); - KafkaConnectApi api = new MockKafkaConnectApi(vertx, statusResults); - Checkpoint async = context.checkpoint(); + KafkaConnectApi api = new MockKafkaConnectApi(statusResults); api.statusWithBackOff(Reconciliation.DUMMY_RECONCILIATION, backOff, "some-host", 8083, "some-connector") - .onComplete(context.succeeding(res -> async.flag())); + .whenComplete((r, e) -> assertThat(e, nullValue())).join(); } @Test - public void testStatusWithBackOffFailingRepeatedly(VertxTestContext context) { - Queue>> statusResults = new ArrayBlockingQueue<>(4); - statusResults.add(Future.failedFuture(new ConnectRestException(null, null, 404, null, null))); - statusResults.add(Future.failedFuture(new ConnectRestException(null, null, 404, null, null))); - statusResults.add(Future.failedFuture(new ConnectRestException(null, null, 404, null, null))); - statusResults.add(Future.failedFuture(new ConnectRestException(null, null, 404, null, null))); - - KafkaConnectApi api = new MockKafkaConnectApi(vertx, statusResults); - Checkpoint async = context.checkpoint(); - - api.statusWithBackOff(Reconciliation.DUMMY_RECONCILIATION, backOff, "some-host", 8083, "some-connector") - .onComplete(context.failing(res -> async.flag())); + public void testStatusWithBackOffFailingRepeatedly() { + Queue>> statusResults = new ArrayBlockingQueue<>(4); + statusResults.add(CompletableFuture.failedFuture(new CompletionException(new ConnectRestException(null, null, 404, null, null)))); + statusResults.add(CompletableFuture.failedFuture(new CompletionException(new ConnectRestException(null, null, 404, null, null)))); + statusResults.add(CompletableFuture.failedFuture(new CompletionException(new ConnectRestException(null, null, 404, null, null)))); + statusResults.add(CompletableFuture.failedFuture(new CompletionException(new ConnectRestException(null, null, 404, null, null)))); + + KafkaConnectApi api = new MockKafkaConnectApi(statusResults); + assertThrows(Exception.class, api.statusWithBackOff(Reconciliation.DUMMY_RECONCILIATION, backOff, "some-host", 8083, "some-connector") + .whenComplete((r, e) -> { + assertThat(e.getMessage(), containsString("404")); + assertThat(statusResults.size(), is(0)); + })::join); } @Test - public void testStatusWithBackOffOtherExceptionStillFails(VertxTestContext context) { - Queue>> statusResults = new ArrayBlockingQueue<>(1); - statusResults.add(Future.failedFuture(new ConnectRestException(null, null, 500, null, null))); + public void testStatusWithBackOffOtherExceptionStillFails() { + Queue>> statusResults = new ArrayBlockingQueue<>(1); + statusResults.add(CompletableFuture.failedFuture(new CompletionException(new ConnectRestException(null, null, 500, null, null)))); - KafkaConnectApi api = new MockKafkaConnectApi(vertx, statusResults); - Checkpoint async = context.checkpoint(); + KafkaConnectApi api = new MockKafkaConnectApi(statusResults); - api.statusWithBackOff(Reconciliation.DUMMY_RECONCILIATION, backOff, "some-host", 8083, "some-connector") - .onComplete(context.failing(res -> async.flag())); + assertThrows(Exception.class, api.statusWithBackOff(Reconciliation.DUMMY_RECONCILIATION, backOff, "some-host", 8083, "some-connector") + .whenComplete((r, e) -> assertThat(e.getMessage(), containsString("500")))::join); } static class MockKafkaConnectApi extends KafkaConnectApiImpl { - private final Queue>> statusResults; + private final Queue>> statusResults; - public MockKafkaConnectApi(Vertx vertx, Queue>> statusResults) { - super(vertx); + public MockKafkaConnectApi(Queue>> statusResults) { + super(); this.statusResults = statusResults; } @Override - public Future> status(Reconciliation reconciliation, String host, int port, String connectorName) { + public CompletableFuture> status(Reconciliation reconciliation, String host, int port, String connectorName) { return statusResults.remove(); } } diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorAutoRestartTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorAutoRestartTest.java index 845eefa5f67..899d4728d05 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorAutoRestartTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorAutoRestartTest.java @@ -29,6 +29,7 @@ import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; @@ -138,7 +139,7 @@ public void testAutoRestartWhenEnabledAndFailedFirstRestart(VertxTestContext con ResourceOperatorSupplier supplier = ResourceUtils.supplierWithMocks(false); KafkaConnectApi mockConnectApi = mock(KafkaConnectApi.class); - when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(Future.succeededFuture(Map.of())); + when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(CompletableFuture.completedFuture(Map.of())); AbstractConnectOperator.ConnectorStatusAndConditions statusAndConditions = new AbstractConnectOperator.ConnectorStatusAndConditions(Map.of("connector", Map.of("state", "FAILED")), List.of(), List.of(), null); KafkaConnector connector = new KafkaConnectorBuilder() .withNewMetadata() @@ -183,7 +184,7 @@ public void testAutoRestartWhenEnabledAndFailedSecondRestart(VertxTestContext co ResourceOperatorSupplier supplier = ResourceUtils.supplierWithMocks(false); KafkaConnectApi mockConnectApi = mock(KafkaConnectApi.class); - when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(Future.succeededFuture(Map.of())); + when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(CompletableFuture.completedFuture(Map.of())); AbstractConnectOperator.ConnectorStatusAndConditions statusAndConditions = new AbstractConnectOperator.ConnectorStatusAndConditions(Map.of("connector", Map.of("state", "FAILED")), List.of(), List.of(), null); KafkaConnector connector = new KafkaConnectorBuilder() .withNewMetadata() diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorOffsetsTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorOffsetsTest.java index d7aacada5ff..f71ad6bdf20 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorOffsetsTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorConnectorOffsetsTest.java @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasEntry; @@ -137,7 +138,7 @@ public void testListOffsetsApiCallFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.failedFuture(new RuntimeException("Rest API call failed"))); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.failedFuture(new RuntimeException("Rest API call failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -166,7 +167,7 @@ public void testListOffsetsGetConfigMapFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.failedFuture(new RuntimeException("Failed to get ConfigMap"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -195,7 +196,7 @@ public void testListOffsetsCreateOrUpdateConfigMapFails(VertxTestContext context supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Create or update ConfigMap failed"))); @@ -225,7 +226,7 @@ public void testListOffsetsRemoveAnnotationFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(supplier.kafkaConnectorOperator.patchAsync(any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Patch CR failed"))); @@ -258,7 +259,7 @@ public void testListOffsetsSucceeds(VertxTestContext context) { - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(supplier.kafkaConnectorOperator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); @@ -317,7 +318,7 @@ public void testListOffsetsDoesNotOverwriteExisting(VertxTestContext context) { .withData(existingData) .build(); - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(existingCM)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(supplier.kafkaConnectorOperator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); @@ -390,7 +391,7 @@ public void testAlterOffsetsStatusApiCallFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.failedFuture(new RuntimeException("Rest API call failed"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.failedFuture(new RuntimeException("Rest API call failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -418,7 +419,7 @@ public void testAlterOffsetsStatusApiCallMissingState(VertxTestContext context) supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(Map.of("foo", "bar"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(Map.of("foo", "bar"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -446,7 +447,7 @@ public void testAlterOffsetsGetConfigMapFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.failedFuture(new RuntimeException("Failed to get ConfigMap"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -476,7 +477,7 @@ public void testAlterOffsetsConfigMapMissing(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -510,7 +511,7 @@ public void testAlterOffsetsCMMissingData(VertxTestContext context) { .withData(Map.of("foo", "bar")) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -544,7 +545,7 @@ public void testAlterOffsetsCMDataInvalid(VertxTestContext context) { .withData(Map.of("offsets.json", "{\"test\":}")) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -578,9 +579,9 @@ public void testAlterOffsetsRemoveAnnotationFails(VertxTestContext context) { .withData(Map.of("offsets.json", OFFSETS_JSON)) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); - when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME, OFFSETS_JSON)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME, OFFSETS_JSON)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.kafkaConnectorOperator.patchAsync(any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Patch CR failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -613,9 +614,9 @@ public void testAlterOffsetsSucceeds(VertxTestContext context) { .withData(Map.of("offsets.json", OFFSETS_JSON)) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); - when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME, OFFSETS_JSON)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME, OFFSETS_JSON)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.kafkaConnectorOperator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -649,7 +650,7 @@ public void testResetOffsetsStatusApiCallFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.failedFuture(new RuntimeException("Rest API call failed"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.failedFuture(new RuntimeException("Rest API call failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -676,7 +677,7 @@ public void testResetOffsetsStatusApiCallMissingState(VertxTestContext context) supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(Map.of("foo", "bar"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(Map.of("foo", "bar"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -703,8 +704,8 @@ public void testResetOffsetsRemoveAnnotationFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); - when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.kafkaConnectorOperator.patchAsync(any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Patch CR failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) @@ -732,8 +733,8 @@ public void testResetOffsetsSucceeds(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig()); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); - when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, CONNECTOR_NAME)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.kafkaConnectorOperator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, CONNECTOR_NAME, connector, connector.getSpec(), new ArrayList<>()) diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorMockTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorMockTest.java index 54a5ad6b1f4..1af9ce7f9cb 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorMockTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorMockTest.java @@ -41,6 +41,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static java.util.Collections.emptyList; import static java.util.Collections.singletonMap; @@ -152,8 +153,8 @@ public void testReconcileCreateAndUpdate(VertxTestContext context) { .endSpec() .build()).create(); KafkaConnectApi mock = mock(KafkaConnectApi.class); - when(mock.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mock.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); + when(mock.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mock.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); Checkpoint async = context.checkpoint(); createConnectCluster(context, mock, false) @@ -181,8 +182,8 @@ public void testPauseReconcileUnpause(VertxTestContext context) { .endSpec() .build()).create(); KafkaConnectApi mock = mock(KafkaConnectApi.class); - when(mock.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mock.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); + when(mock.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mock.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); Checkpoint async = context.checkpoint(); createConnectCluster(context, mock, true) diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorPodSetTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorPodSetTest.java index e45c1c75335..900c9cf1b9b 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorPodSetTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperatorPodSetTest.java @@ -74,6 +74,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiPredicate; @@ -190,14 +191,14 @@ public void testCreateCluster(VertxTestContext context) { // Mock Connect REST API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); ConnectorPlugin plugin1 = new ConnectorPluginBuilder() .withConnectorClass("io.strimzi.MyClass") .withType("sink") .withVersion("1.0.0") .build(); - when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(singletonList(plugin1))); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(singletonList(plugin1))); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaConnectAssemblyOperator ops = new KafkaConnectAssemblyOperator( vertx, @@ -327,14 +328,14 @@ public void testCreateClusterWithConnectorOperator(VertxTestContext context) { // Mock Connect REST API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); ConnectorPlugin plugin1 = new ConnectorPluginBuilder() .withConnectorClass("io.strimzi.MyClass") .withType("sink") .withVersion("1.0.0") .build(); - when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(singletonList(plugin1))); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(singletonList(plugin1))); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaConnectAssemblyOperator ops = new KafkaConnectAssemblyOperator( vertx, @@ -956,14 +957,14 @@ public void testUpdateClusterWithConnectors(VertxTestContext context) { // Mock Connect REST API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); ConnectorPlugin plugin1 = new ConnectorPluginBuilder() .withConnectorClass("io.strimzi.MyClass") .withType("sink") .withVersion("1.0.0") .build(); - when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(singletonList(plugin1))); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(singletonList(plugin1))); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaConnectAssemblyOperator ops = new KafkaConnectAssemblyOperator( vertx, @@ -1197,14 +1198,14 @@ public void testUpdateLoggingWithConnectors(VertxTestContext context) { // Mock Connect REST API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); ConnectorPlugin plugin1 = new ConnectorPluginBuilder() .withConnectorClass("io.strimzi.MyClass") .withType("sink") .withVersion("1.0.0") .build(); - when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(singletonList(plugin1))); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.listConnectorPlugins(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(singletonList(plugin1))); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaConnectAssemblyOperator ops = new KafkaConnectAssemblyOperator( vertx, diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectorIT.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectorIT.java index 94e0aecba5f..3717cfab9c1 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectorIT.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectorIT.java @@ -131,7 +131,7 @@ public void afterEach() { @Test public void testConnectorNotUpdatedWhenConfigUnchanged(VertxTestContext context) { - KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(vertx); + KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(); PlatformFeaturesAvailability pfa = new PlatformFeaturesAvailability(false, KubernetesVersion.MINIMAL_SUPPORTED_VERSION); @@ -165,7 +165,7 @@ public void testConnectorNotUpdatedWhenConfigUnchanged(VertxTestContext context) KafkaConnectAssemblyOperator operator = new KafkaConnectAssemblyOperator(vertx, pfa, ros, ClusterOperatorConfig.buildFromMap(Map.of(), KafkaVersionTestUtils.getKafkaVersionLookup()), - connect -> new KafkaConnectApiImpl(vertx), + connect -> new KafkaConnectApiImpl(), connectCluster.getPort(0) ) { }; @@ -200,8 +200,8 @@ public void testConnectorNotUpdatedWhenConfigUnchanged(VertxTestContext context) } @Test - public void testConnectorResourceNotReadyWhenConnectorFailed(VertxTestContext context) { - KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(vertx); + public void testConnectorResourceNotReadyWhenConnectorFailed(VertxTestContext context) { + KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(); PlatformFeaturesAvailability pfa = new PlatformFeaturesAvailability(false, KubernetesVersion.MINIMAL_SUPPORTED_VERSION); @@ -236,7 +236,7 @@ public void testConnectorResourceNotReadyWhenConnectorFailed(VertxTestContext co KafkaConnectAssemblyOperator operator = new KafkaConnectAssemblyOperator(vertx, pfa, ros, ClusterOperatorConfig.buildFromMap(Map.of(), KafkaVersionTestUtils.getKafkaVersionLookup()), - connect -> new KafkaConnectApiImpl(vertx), + connect -> new KafkaConnectApiImpl(), connectCluster.getPort(0) ) { }; @@ -251,7 +251,7 @@ public void testConnectorResourceNotReadyWhenConnectorFailed(VertxTestContext co @Test public void testConnectorResourceNotReadyWhenTaskFailed(VertxTestContext context) { - KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(vertx); + KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(); PlatformFeaturesAvailability pfa = new PlatformFeaturesAvailability(false, KubernetesVersion.MINIMAL_SUPPORTED_VERSION); @@ -286,7 +286,7 @@ public void testConnectorResourceNotReadyWhenTaskFailed(VertxTestContext context KafkaConnectAssemblyOperator operator = new KafkaConnectAssemblyOperator(vertx, pfa, ros, ClusterOperatorConfig.buildFromMap(Map.of(), KafkaVersionTestUtils.getKafkaVersionLookup()), - connect -> new KafkaConnectApiImpl(vertx), + connect -> new KafkaConnectApiImpl(), connectCluster.getPort(0) ) { }; @@ -312,7 +312,7 @@ public void testConnectorResourceNotReadyWhenTaskFailed(VertxTestContext context @Test public void testConnectorIsAutoRestarted(VertxTestContext context) { - KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(vertx); + KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(); PlatformFeaturesAvailability pfa = new PlatformFeaturesAvailability(false, KubernetesVersion.MINIMAL_SUPPORTED_VERSION); @@ -347,7 +347,7 @@ public void testConnectorIsAutoRestarted(VertxTestContext context) { KafkaConnectAssemblyOperator operator = new KafkaConnectAssemblyOperator(vertx, pfa, ros, ClusterOperatorConfig.buildFromMap(Map.of(), KafkaVersionTestUtils.getKafkaVersionLookup()), - connect -> new KafkaConnectApiImpl(vertx), + connect -> new KafkaConnectApiImpl(), connectCluster.getPort(0) ) { }; @@ -362,7 +362,7 @@ public void testConnectorIsAutoRestarted(VertxTestContext context) { @Test public void testTaskIsAutoRestarted(VertxTestContext context) { - KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(vertx); + KafkaConnectApiImpl connectClient = new KafkaConnectApiImpl(); PlatformFeaturesAvailability pfa = new PlatformFeaturesAvailability(false, KubernetesVersion.MINIMAL_SUPPORTED_VERSION); @@ -397,7 +397,7 @@ public void testTaskIsAutoRestarted(VertxTestContext context) { KafkaConnectAssemblyOperator operator = new KafkaConnectAssemblyOperator(vertx, pfa, ros, ClusterOperatorConfig.buildFromMap(Map.of(), KafkaVersionTestUtils.getKafkaVersionLookup()), - connect -> new KafkaConnectApiImpl(vertx), + connect -> new KafkaConnectApiImpl(), connectCluster.getPort(0) ) { }; diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorAutoRestartTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorAutoRestartTest.java index 58621eac35f..0138d8f012d 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorAutoRestartTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorAutoRestartTest.java @@ -20,7 +20,6 @@ import io.strimzi.operator.cluster.operator.resource.ResourceOperatorSupplier; import io.strimzi.operator.common.Reconciliation; import io.strimzi.platform.KubernetesVersion; -import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.junit5.Checkpoint; import io.vertx.junit5.VertxExtension; @@ -37,6 +36,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; @@ -107,12 +107,12 @@ public static void after() { private KafkaConnectApi mockConnectApi(Map status) { KafkaConnectApi mockConnectApi = mock(KafkaConnectApi.class); - when(mockConnectApi.list(any(), any(), anyInt())).thenReturn(Future.succeededFuture(new ArrayList<>())); - when(mockConnectApi.getConnectorConfig(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(EXPECTED_CONNECTOR_CONFIG)); - when(mockConnectApi.status(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(status)); - when(mockConnectApi.statusWithBackOff(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(status)); - when(mockConnectApi.getConnectorTopics(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(List.of())); - when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(Future.succeededFuture(Map.of("connector", Map.of("state", "RESTARTING")))); + when(mockConnectApi.list(any(), any(), anyInt())).thenReturn(CompletableFuture.completedFuture(new ArrayList<>())); + when(mockConnectApi.getConnectorConfig(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(EXPECTED_CONNECTOR_CONFIG)); + when(mockConnectApi.status(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(status)); + when(mockConnectApi.statusWithBackOff(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(status)); + when(mockConnectApi.getConnectorTopics(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(List.of())); + when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(CompletableFuture.completedFuture(Map.of("connector", Map.of("state", "RESTARTING")))); return mockConnectApi; } diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorOffsetsTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorOffsetsTest.java index 63adf83a31d..926a325b3e8 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorOffsetsTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorConnectorOffsetsTest.java @@ -49,6 +49,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; @@ -271,7 +272,7 @@ public void testListOffsetsApiCallFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.failedFuture(new RuntimeException("Rest API call failed"))); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.failedFuture(new RuntimeException("Rest API call failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -308,7 +309,7 @@ public void testListOffsetsGetConfigMapFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.failedFuture(new RuntimeException("Failed to get ConfigMap"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -345,7 +346,7 @@ public void testListOffsetsCreateOrUpdateConfigMapFails(VertxTestContext context supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Create or update ConfigMap failed"))); @@ -383,7 +384,7 @@ public void testListOffsetsRemoveAnnotationFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(supplier.mirrorMaker2Operator.patchAsync(any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Patch CR failed"))); @@ -423,7 +424,7 @@ public void testListOffsetsSucceeds(String connector, VertxTestContext context) supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(supplier.mirrorMaker2Operator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); @@ -492,7 +493,7 @@ public void testListOffsetsDoesNotOverwriteExisting(String connector, VertxTestC .withData(existingData) .build(); - when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(OFFSETS_JSON)); + when(mockConnectApi.getConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(OFFSETS_JSON)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(existingCM)); when(supplier.configMapOperations.reconcile(any(), any(), any(), any())).thenReturn(Future.succeededFuture()); when(supplier.mirrorMaker2Operator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); @@ -615,7 +616,7 @@ public void testAlterOffsetsStatusApiCallFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.failedFuture(new RuntimeException("Rest API call failed"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.failedFuture(new RuntimeException("Rest API call failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -651,7 +652,7 @@ public void testAlterOffsetsStatusApiCallMissingState(VertxTestContext context) supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(Map.of("foo", "bar"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(Map.of("foo", "bar"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -687,7 +688,7 @@ public void testAlterOffsetsGetConfigMapFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.failedFuture(new RuntimeException("Failed to get ConfigMap"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -725,7 +726,7 @@ public void testAlterOffsetsConfigMapMissing(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(null)); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -768,7 +769,7 @@ public void testAlterOffsetsCMMissingData(VertxTestContext context) { .withData(Map.of("foo", "bar")) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -811,7 +812,7 @@ public void testAlterOffsetsCMDataInvalid(VertxTestContext context) { .withData(Map.of(getConfigmapEntryName(connector), "{\"test\":}")) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -854,9 +855,9 @@ public void testAlterOffsetsRemoveAnnotationFails(VertxTestContext context) { .withData(Map.of(getConfigmapEntryName(connector), OFFSETS_JSON)) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); - when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName, OFFSETS_JSON)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName, OFFSETS_JSON)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.mirrorMaker2Operator.patchAsync(any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Patch CR failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -898,9 +899,9 @@ public void testAlterOffsetsSucceeds(String connector, VertxTestContext context) .withData(Map.of(getConfigmapEntryName(connector), OFFSETS_JSON)) .build(); - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); when(supplier.configMapOperations.getAsync(NAMESPACE, CONFIGMAP_NAME)).thenReturn(Future.succeededFuture(alterOffsetsConfigMap)); - when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName, OFFSETS_JSON)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.alterConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName, OFFSETS_JSON)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.mirrorMaker2Operator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -975,7 +976,7 @@ public void testResetOffsetsStatusApiCallFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.failedFuture(new RuntimeException("Rest API call failed"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.failedFuture(new RuntimeException("Rest API call failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -1010,7 +1011,7 @@ public void testResetOffsetsStatusApiCallMissingState(VertxTestContext context) supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(Map.of("foo", "bar"))); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(Map.of("foo", "bar"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) .onComplete(context.succeeding(result -> context.verify(() -> { @@ -1045,8 +1046,8 @@ public void testResetOffsetsRemoveAnnotationFails(VertxTestContext context) { supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); - when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.mirrorMaker2Operator.patchAsync(any(), any())).thenReturn(Future.failedFuture(new RuntimeException("Patch CR failed"))); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) @@ -1083,8 +1084,8 @@ public void testResetOffsetsSucceeds(String connector, VertxTestContext context) supplier, ResourceUtils.dummyClusterOperatorConfig(), (vertx) -> mockConnectApi); Reconciliation reconciliation = Reconciliation.DUMMY_RECONCILIATION; - when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture(STOPPED_STATE_MAP)); - when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(Future.succeededFuture()); + when(mockConnectApi.status(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(STOPPED_STATE_MAP)); + when(mockConnectApi.resetConnectorOffsets(reconciliation, CONNECT_HOSTNAME, KafkaConnectCluster.REST_API_PORT, connectorName)).thenReturn(CompletableFuture.completedFuture(null)); when(supplier.mirrorMaker2Operator.patchAsync(any(), any())).thenReturn(Future.succeededFuture()); op.manageConnectorOffsets(reconciliation, CONNECT_HOSTNAME, mockConnectApi, connectorName, kafkaMirrorMaker2, kafkaMirrorMaker2SourceConnector.getSpec(), new ArrayList<>()) diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorMockTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorMockTest.java index 467b46f6ad8..0235f39d85c 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorMockTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorMockTest.java @@ -58,6 +58,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static java.util.Collections.emptyList; import static java.util.Collections.singletonMap; @@ -207,21 +208,21 @@ private KafkaConnectApi mockConnectApi(String state) { connectorOffsets = LIST_OFFSETS_JSON; Map status = Map.of("connector", Map.of("state", state)); KafkaConnectApi mockConnectApi = mock(KafkaConnectApi.class); - when(mockConnectApi.list(any(), any(), anyInt())).thenReturn(Future.succeededFuture(new ArrayList<>())); - when(mockConnectApi.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); - when(mockConnectApi.getConnectorConfig(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(EXPECTED_CONNECTOR_CONFIG)); - when(mockConnectApi.status(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(status)); - when(mockConnectApi.statusWithBackOff(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(status)); - when(mockConnectApi.getConnectorTopics(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(Future.succeededFuture(List.of())); - when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(Future.succeededFuture(Map.of("connector", Map.of("state", "RESTARTING")))); - when(mockConnectApi.getConnectorOffsets(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> Future.succeededFuture(connectorOffsets)); + when(mockConnectApi.list(any(), any(), anyInt())).thenReturn(CompletableFuture.completedFuture(new ArrayList<>())); + when(mockConnectApi.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); + when(mockConnectApi.getConnectorConfig(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(EXPECTED_CONNECTOR_CONFIG)); + when(mockConnectApi.status(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(status)); + when(mockConnectApi.statusWithBackOff(any(), any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(status)); + when(mockConnectApi.getConnectorTopics(any(), any(), anyInt(), eq("source->target.MirrorSourceConnector"))).thenReturn(CompletableFuture.completedFuture(List.of())); + when(mockConnectApi.restart(any(), anyInt(), any(), anyBoolean(), anyBoolean())).thenReturn(CompletableFuture.completedFuture(Map.of("connector", Map.of("state", "RESTARTING")))); + when(mockConnectApi.getConnectorOffsets(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> CompletableFuture.completedFuture(connectorOffsets)); when(mockConnectApi.alterConnectorOffsets(any(), any(), anyInt(), anyString(), anyString())).thenAnswer(invocation -> { connectorOffsets = invocation.getArgument(4); - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); when(mockConnectApi.resetConnectorOffsets(any(), any(), anyInt(), anyString())).thenAnswer(invocation -> { connectorOffsets = RESET_OFFSETS_JSON; - return Future.succeededFuture(); + return CompletableFuture.completedFuture(null); }); return mockConnectApi; @@ -245,8 +246,8 @@ public void testReconcileUpdate(VertxTestContext context) { .build()).create(); KafkaConnectApi mock = mock(KafkaConnectApi.class); - when(mock.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mock.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mock.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mock.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); Checkpoint async = context.checkpoint(); createMirrorMaker2Cluster(context, mock, false) @@ -277,8 +278,8 @@ public void testPauseReconcile(VertxTestContext context) { .build()).create(); KafkaConnectApi mock = mock(KafkaConnectApi.class); - when(mock.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mock.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mock.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mock.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); Checkpoint async = context.checkpoint(); createMirrorMaker2Cluster(context, mock, true) diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorPodSetTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorPodSetTest.java index 503c8bf36bd..9da2f9c117e 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorPodSetTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperatorPodSetTest.java @@ -68,6 +68,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; @@ -181,8 +182,8 @@ public void testCreateCluster(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -302,8 +303,8 @@ public void testScaleUpCluster(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -399,8 +400,8 @@ public void testScaleDownCluster(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -501,8 +502,8 @@ public void testScaleClusterToZero(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -598,8 +599,8 @@ public void testUpdateClusterNoDiff(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -717,8 +718,8 @@ public void testUpdateCluster(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -835,8 +836,8 @@ public void testUpdateWithFailure(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -973,8 +974,8 @@ public void testClusterMigrationToPodSets(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -1254,8 +1255,8 @@ public void testManualRollingUpdate(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -1342,8 +1343,8 @@ public void testManualRollingUpdateAtScaleUp(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -1431,8 +1432,8 @@ public void testManualRollingUpdatePerPod(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -1520,8 +1521,8 @@ public void testFailingManualRollingUpdate(VertxTestContext context) { // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); ClusterOperatorConfig coConfig = new ClusterOperatorConfig.ClusterOperatorConfigBuilder(ResourceUtils.dummyClusterOperatorConfig(), VERSIONS).with(ClusterOperatorConfig.FEATURE_GATES.key(), "-ContinueReconciliationOnManualRollingUpdateFailure").build(); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( @@ -1617,8 +1618,8 @@ public void testManualRollingUpdateWithSuppressedFailure(VertxTestContext contex // Mock Connect API KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); KafkaMirrorMaker2AssemblyOperator ops = new KafkaMirrorMaker2AssemblyOperator( vertx, @@ -1646,8 +1647,8 @@ public void testManualRollingUpdateWithSuppressedFailure(VertxTestContext contex private KafkaConnectApi createConnectClientMock() { KafkaConnectApi mockConnectClient = mock(KafkaConnectApi.class); - when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(Future.succeededFuture(emptyList())); - when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(Future.succeededFuture()); + when(mockConnectClient.list(any(), anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(emptyList())); + when(mockConnectClient.updateConnectLoggers(any(), anyString(), anyInt(), anyString(), any(OrderedProperties.class))).thenReturn(CompletableFuture.completedFuture(null)); return mockConnectClient; } }