diff --git a/pom.xml b/pom.xml
index 43a35a70a..657f11500 100644
--- a/pom.xml
+++ b/pom.xml
@@ -473,6 +473,7 @@
org.jboss.logmanager.LogManager
${maven.home}
+ ${skip.ut}
diff --git a/src/test/java/io/cryostat/resources/AgentApplicationResource.java b/src/test/java/io/cryostat/resources/AgentApplicationResource.java
new file mode 100644
index 000000000..c8cdbc62b
--- /dev/null
+++ b/src/test/java/io/cryostat/resources/AgentApplicationResource.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright The Cryostat Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.cryostat.resources;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import io.quarkus.test.common.DevServicesContext;
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.jboss.logging.Logger;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.Network;
+import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
+import org.testcontainers.utility.DockerImageName;
+
+public class AgentApplicationResource
+ implements QuarkusTestResourceLifecycleManager, DevServicesContext.ContextAware {
+
+ private static final String IMAGE_NAME =
+ "quay.io/redhat-java-monitoring/quarkus-cryostat-agent:latest";
+ public static final int PORT = 9977;
+ public static final String ALIAS = "quarkus-cryostat-agent";
+ private static final Map envMap =
+ new HashMap<>(
+ Map.of(
+ "JAVA_OPTS_APPEND",
+ """
+ -javaagent:/deployments/app/cryostat-agent.jar
+ -Djava.util.logging.manager=org.jboss.logmanager.LogManager
+ -Dio.cryostat.agent.shaded.org.slf4j.simpleLogger.defaultLogLevel=trace
+ """,
+ "QUARKUS_HTTP_PORT",
+ "9898",
+ "CRYOSTAT_AGENT_APP_NAME",
+ "quarkus-cryostat-agent",
+ "CRYOSTAT_AGENT_WEBCLIENT_TLS_REQUIRED",
+ "false",
+ "CRYOSTAT_AGENT_WEBSERVER_HOST",
+ "0.0.0.0",
+ "CRYOSTAT_AGENT_WEBSERVER_PORT",
+ Integer.toString(PORT),
+ "CRYOSTAT_AGENT_BASEURI_RANGE",
+ "public",
+ "CRYOSTAT_AGENT_API_WRITES_ENABLED",
+ "true"));
+ private static final Logger logger = Logger.getLogger(AgentApplicationResource.class);
+ private Optional containerNetworkId;
+ private AuthProxyContainer authProxy;
+ private GenericContainer> container;
+ private AtomicInteger cryostatPort = new AtomicInteger(8081);
+
+ @Override
+ public Map start() {
+ Optional network =
+ containerNetworkId.map(
+ id ->
+ new Network() {
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public Statement apply(
+ Statement base, Description description) {
+ throw new UnsupportedOperationException(
+ "Unimplemented method 'apply'");
+ }
+ });
+ authProxy = new AuthProxyContainer(network, cryostatPort.get());
+
+ container =
+ new GenericContainer<>(DockerImageName.parse(IMAGE_NAME))
+ .dependsOn(authProxy)
+ .withExposedPorts(PORT)
+ .withEnv(envMap)
+ .withNetworkAliases(ALIAS)
+ .waitingFor(new HostPortWaitStrategy().forPorts(PORT));
+ network.ifPresent(container::withNetwork);
+ container.addEnv(
+ "CRYOSTAT_AGENT_BASEURI",
+ String.format("http://%s:%d/", AuthProxyContainer.ALIAS, AuthProxyContainer.PORT));
+ container.addEnv("CRYOSTAT_AGENT_CALLBACK", String.format("http://%s:%d/", ALIAS, PORT));
+
+ container.start();
+
+ return Map.of(
+ "quarkus.test.arg-line", "--network-alias=cryostat",
+ "cryostat.agent.tls.required", "false",
+ "cryostat.http.proxy.host", ALIAS,
+ "cryostat.http.proxy.port", Integer.toString(cryostatPort.get()),
+ "quarkus.http.proxy.proxy-address-forwarding", "true",
+ "quarkus.http.proxy.allow-x-forwarded", "true",
+ "quarkus.http.proxy.enable-forwarded-host", "true",
+ "quarkus.http.proxy.enable-forwarded-prefix", "true",
+ "quarkus.http.access-log.pattern", "long",
+ "quarkus.http.access-log.enabled", "true");
+ }
+
+ @Override
+ public void stop() {
+ if (container != null) {
+ container.stop();
+ container.close();
+ }
+ if (authProxy != null) {
+ authProxy.stop();
+ authProxy.close();
+ }
+ }
+
+ @Override
+ public void setIntegrationTestContext(DevServicesContext context) {
+ containerNetworkId = context.containerNetworkId();
+ cryostatPort.set(
+ Integer.parseInt(
+ context.devServicesProperties().getOrDefault("quarkus.http.port", "8081")));
+ }
+}
diff --git a/src/test/java/io/cryostat/resources/AuthProxyContainer.java b/src/test/java/io/cryostat/resources/AuthProxyContainer.java
new file mode 100644
index 000000000..4f705f54c
--- /dev/null
+++ b/src/test/java/io/cryostat/resources/AuthProxyContainer.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright The Cryostat Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.cryostat.resources;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.Network;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.Transferable;
+
+public class AuthProxyContainer extends GenericContainer {
+
+ private static final String IMAGE_NAME = "quay.io/oauth2-proxy/oauth2-proxy:latest";
+ private static final String CFG_FILE_PATH = "/tmp/auth_proxy_alpha_config.yml";
+ static final int PORT = 8080;
+ static final String ALIAS = "authproxy";
+ private static final Map envMap =
+ new HashMap<>(
+ Map.of(
+ "OAUTH2_PROXY_REDIRECT_URL",
+ "http://localhost:8080/oauth2/callback",
+ "OAUTH2_PROXY_COOKIE_SECRET",
+ "__24_BYTE_COOKIE_SECRET_",
+ "OAUTH2_PROXY_SKIP_AUTH_ROUTES",
+ ".*",
+ "OAUTH2_PROXY_EMAIL_DOMAINS",
+ "*"));
+ private static final String ALPHA_CONFIG =
+ """
+server:
+ BindAddress: http://AUTHPROXY_HOST:AUTHPROXY_PORT
+upstreamConfig:
+ proxyRawPath: true
+ upstreams:
+ - id: cryostat
+ path: /
+ uri: http://cryostat:CRYOSTAT_PORT
+providers:
+ - id: dummy
+ name: Unused - Sign In Below
+ clientId: CLIENT_ID
+ clientSecret: CLIENT_SECRET
+ provider: google
+""";
+
+ public AuthProxyContainer(Optional network, int cryostatPort) {
+ super(IMAGE_NAME);
+ network.ifPresent(this::withNetwork);
+ withCommand(String.format("--alpha-config=%s", CFG_FILE_PATH));
+ withExposedPorts(PORT);
+ withNetworkAliases(ALIAS);
+ withEnv(envMap);
+ withCopyToContainer(
+ Transferable.of(
+ ALPHA_CONFIG
+ .replaceAll("AUTHPROXY_HOST", "0.0.0.0")
+ .replaceAll("AUTHPROXY_PORT", Integer.toString(PORT))
+ .replaceAll("CRYOSTAT_PORT", Integer.toString(cryostatPort))),
+ CFG_FILE_PATH);
+ waitingFor(Wait.forLogMessage(".*OAuthProxy configured.*", 1));
+ }
+}
diff --git a/src/test/java/itest/AgentDiscoveryIT.java b/src/test/java/itest/AgentDiscoveryIT.java
new file mode 100644
index 000000000..009652bdd
--- /dev/null
+++ b/src/test/java/itest/AgentDiscoveryIT.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright The Cryostat Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package itest;
+
+import java.time.Duration;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import io.cryostat.resources.AgentApplicationResource;
+import io.cryostat.util.HttpStatusCodeIdentifier;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.client.HttpResponse;
+import itest.bases.HttpClientTest;
+import junit.framework.AssertionFailedError;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.jboss.logging.Logger;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
+
+@QuarkusIntegrationTest
+@QuarkusTestResource(value = AgentApplicationResource.class, restrictToAnnotatedClass = true)
+@EnabledIfEnvironmentVariable(named = "CI_ARCH", matches = "^$")
+@EnabledIfEnvironmentVariable(named = "CI_ARCH", matches = "^amd64|AMD64$")
+public class AgentDiscoveryIT extends HttpClientTest {
+
+ static final Logger logger = Logger.getLogger(AgentDiscoveryIT.class);
+ static final Duration TIMEOUT = Duration.ofSeconds(60);
+
+ @Test
+ void shouldDiscoverTarget() throws InterruptedException, TimeoutException, ExecutionException {
+ long last = System.nanoTime();
+ long elapsed = 0;
+ while (true) {
+ HttpResponse req =
+ webClient.extensions().get("/api/v4/targets", REQUEST_TIMEOUT_SECONDS);
+ if (HttpStatusCodeIdentifier.isSuccessCode(req.statusCode())) {
+ JsonArray result = req.bodyAsJsonArray();
+ if (result.size() == 1) {
+ JsonObject obj = result.getJsonObject(0);
+ MatcherAssert.assertThat(
+ obj.getString("alias"),
+ Matchers.equalTo(AgentApplicationResource.ALIAS));
+ MatcherAssert.assertThat(
+ obj.getString("connectUrl"),
+ Matchers.equalTo(
+ String.format(
+ "http://%s:%d/",
+ AgentApplicationResource.ALIAS,
+ AgentApplicationResource.PORT)));
+
+ MatcherAssert.assertThat(obj.getBoolean("agent"), Matchers.is(true));
+ break;
+ } else if (result.size() > 1) {
+ throw new IllegalStateException("Discovered too many targets");
+ }
+ }
+
+ long now = System.nanoTime();
+ elapsed += (now - last);
+ last = now;
+ if (Duration.ofNanos(elapsed).compareTo(TIMEOUT) > 0) {
+ throw new AssertionFailedError("Timed out");
+ }
+ Thread.sleep(5_000);
+ }
+ }
+}
diff --git a/src/test/java/itest/bases/HttpClientTest.java b/src/test/java/itest/bases/HttpClientTest.java
new file mode 100644
index 000000000..981350b00
--- /dev/null
+++ b/src/test/java/itest/bases/HttpClientTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright The Cryostat Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package itest.bases;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import io.cryostat.util.HttpStatusCodeIdentifier;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.MultiMap;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.file.FileSystem;
+import io.vertx.core.http.WebSocket;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.client.HttpRequest;
+import io.vertx.ext.web.client.HttpResponse;
+import io.vertx.ext.web.handler.HttpException;
+import itest.util.Utils;
+import itest.util.Utils.TestWebClient;
+import org.jboss.logging.Logger;
+
+public abstract class HttpClientTest {
+
+ protected static final ExecutorService WORKER = Executors.newCachedThreadPool();
+ public static final Logger logger = Logger.getLogger(HttpClientTest.class);
+ public static final ObjectMapper mapper;
+ public static final int REQUEST_TIMEOUT_SECONDS = 30;
+ public static final TestWebClient webClient = Utils.getWebClient();
+
+ static {
+ mapper =
+ new ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .setVisibility(PropertyAccessor.ALL, Visibility.ANY);
+ }
+
+ public static CompletableFuture expectNotification(
+ String category, long timeout, TimeUnit unit)
+ throws TimeoutException, ExecutionException, InterruptedException {
+ logger.debugv(
+ "Waiting for a \"{0}\" message within the next {1} {2} ...",
+ category, timeout, unit.name());
+ CompletableFuture future = new CompletableFuture<>();
+
+ var a = new WebSocket[1];
+ Utils.HTTP_CLIENT.webSocket(
+ "ws://localhost/api/notifications",
+ ar -> {
+ if (ar.failed()) {
+ future.completeExceptionally(ar.cause());
+ return;
+ }
+ a[0] = ar.result();
+ var ws = a[0];
+
+ ws.handler(
+ m -> {
+ JsonObject resp = m.toJsonObject();
+ JsonObject meta = resp.getJsonObject("meta");
+ String c = meta.getString("category");
+ if (Objects.equals(c, category)) {
+ logger.tracev(
+ "Received expected \"{0}\" message", category);
+ ws.end(unused -> future.complete(resp));
+ ws.close();
+ }
+ })
+ // FIXME in the cryostat itests we DO use auth. The message below is
+ // copy-pasted from the old codebase, however cryostat does not yet
+ // perform authentication when websocket clients connect.
+
+ // just to initialize the connection - Cryostat expects
+ // clients to send a message after the connection opens
+ // to authenticate themselves, but in itests we don't
+ // use auth
+ .writeTextMessage("");
+ });
+
+ return future.orTimeout(timeout, unit).whenComplete((o, t) -> a[0].close());
+ }
+
+ public static boolean assertRequestStatus(
+ AsyncResult> result, CompletableFuture> future) {
+ if (result.failed()) {
+ result.cause().printStackTrace();
+ future.completeExceptionally(result.cause());
+
+ return false;
+ }
+ HttpResponse response = result.result();
+ if (!HttpStatusCodeIdentifier.isSuccessCode(response.statusCode())
+ && !HttpStatusCodeIdentifier.isRedirectCode(response.statusCode())) {
+ System.err.println("HTTP " + response.statusCode() + ": " + response.statusMessage());
+ future.completeExceptionally(
+ new HttpException(response.statusCode(), response.statusMessage()));
+ return false;
+ }
+ return true;
+ }
+
+ public static CompletableFuture downloadFile(String url, String name, String suffix) {
+ return fireDownloadRequest(
+ webClient.get(url), name, suffix, MultiMap.caseInsensitiveMultiMap());
+ }
+
+ public static CompletableFuture downloadFile(
+ String url, String name, String suffix, MultiMap headers) {
+ return fireDownloadRequest(webClient.get(url), name, suffix, headers);
+ }
+
+ public static CompletableFuture downloadFileAbs(String url, String name, String suffix) {
+ return fireDownloadRequest(
+ webClient.getAbs(url), name, suffix, MultiMap.caseInsensitiveMultiMap());
+ }
+
+ public static CompletableFuture downloadFileAbs(
+ String url, String name, String suffix, MultiMap headers) {
+ return fireDownloadRequest(webClient.getAbs(url), name, suffix, headers);
+ }
+
+ private static CompletableFuture fireDownloadRequest(
+ HttpRequest request, String filename, String fileSuffix, MultiMap headers) {
+ CompletableFuture future = new CompletableFuture<>();
+ WORKER.submit(
+ () -> {
+ request.putHeaders(headers)
+ .followRedirects(true)
+ .send(
+ ar -> {
+ if (ar.failed()) {
+ future.completeExceptionally(ar.cause());
+ return;
+ }
+ HttpResponse resp = ar.result();
+ logger.tracev(
+ "GET {0} -> HTTP {1} {2}: [{3}]",
+ request.uri(),
+ resp.statusCode(),
+ resp.statusMessage(),
+ resp.headers());
+ if (!(HttpStatusCodeIdentifier.isSuccessCode(
+ resp.statusCode()))) {
+ future.completeExceptionally(
+ new Exception(
+ String.format(
+ "HTTP %d", resp.statusCode())));
+ return;
+ }
+ FileSystem fs = Utils.getFileSystem();
+ String file =
+ fs.createTempFileBlocking(filename, fileSuffix);
+ fs.writeFileBlocking(file, ar.result().body());
+ future.complete(Paths.get(file));
+ });
+ });
+ return future;
+ }
+}
diff --git a/src/test/java/itest/bases/StandardSelfTest.java b/src/test/java/itest/bases/StandardSelfTest.java
index 9b5a3292b..cf53b38fa 100644
--- a/src/test/java/itest/bases/StandardSelfTest.java
+++ b/src/test/java/itest/bases/StandardSelfTest.java
@@ -16,66 +16,37 @@
package itest.bases;
import java.net.URI;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import io.cryostat.util.HttpStatusCodeIdentifier;
-import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.vertx.core.AsyncResult;
-import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
-import io.vertx.core.file.FileSystem;
-import io.vertx.core.http.WebSocket;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
-import io.vertx.ext.web.client.HttpRequest;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.codec.BodyCodec;
-import io.vertx.ext.web.handler.HttpException;
import itest.util.ITestCleanupFailedException;
-import itest.util.Utils;
-import itest.util.Utils.TestWebClient;
import jakarta.ws.rs.core.HttpHeaders;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.jboss.logging.Logger;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
-public abstract class StandardSelfTest {
+public abstract class StandardSelfTest extends HttpClientTest {
public static final String SELF_JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:0/jmxrmi";
public static String SELF_JMX_URL_ENCODED =
URLEncodedUtils.formatSegments(SELF_JMX_URL).substring(1);
public static final String SELFTEST_ALIAS = "selftest";
- private static final ExecutorService WORKER = Executors.newCachedThreadPool();
public static final Logger logger = Logger.getLogger(StandardSelfTest.class);
- public static final ObjectMapper mapper;
- public static final int REQUEST_TIMEOUT_SECONDS = 30;
public static final int DISCOVERY_DEADLINE_SECONDS = 60;
- public static final TestWebClient webClient = Utils.getWebClient();
public static volatile String selfCustomTargetLocation;
- static {
- mapper =
- new ObjectMapper()
- .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
- .setVisibility(PropertyAccessor.ALL, Visibility.ANY);
- }
-
@BeforeAll
public static void waitForDiscovery() {
waitForDiscovery(0);
@@ -262,129 +233,4 @@ public static String getSelfReferenceConnectUrl() {
public static String getSelfReferenceConnectUrlEncoded() {
return URLEncodedUtils.formatSegments(getSelfReferenceConnectUrl()).substring(1);
}
-
- public static final Pair VERTX_FIB_CREDENTIALS =
- Pair.of("admin", "adminpass123");
-
- public static CompletableFuture expectNotification(
- String category, long timeout, TimeUnit unit)
- throws TimeoutException, ExecutionException, InterruptedException {
- logger.debugv(
- "Waiting for a \"{0}\" message within the next {1} {2} ...",
- category, timeout, unit.name());
- CompletableFuture future = new CompletableFuture<>();
-
- var a = new WebSocket[1];
- Utils.HTTP_CLIENT.webSocket(
- "ws://localhost/api/notifications",
- ar -> {
- if (ar.failed()) {
- future.completeExceptionally(ar.cause());
- return;
- }
- a[0] = ar.result();
- var ws = a[0];
-
- ws.handler(
- m -> {
- JsonObject resp = m.toJsonObject();
- JsonObject meta = resp.getJsonObject("meta");
- String c = meta.getString("category");
- if (Objects.equals(c, category)) {
- logger.tracev(
- "Received expected \"{0}\" message", category);
- ws.end(unused -> future.complete(resp));
- ws.close();
- }
- })
- // FIXME in the cryostat itests we DO use auth. The message below is
- // copy-pasted from the old codebase, however cryostat does not yet
- // perform authentication when websocket clients connect.
-
- // just to initialize the connection - Cryostat expects
- // clients to send a message after the connection opens
- // to authenticate themselves, but in itests we don't
- // use auth
- .writeTextMessage("");
- });
-
- return future.orTimeout(timeout, unit).whenComplete((o, t) -> a[0].close());
- }
-
- public static boolean assertRequestStatus(
- AsyncResult> result, CompletableFuture> future) {
- if (result.failed()) {
- result.cause().printStackTrace();
- future.completeExceptionally(result.cause());
-
- return false;
- }
- HttpResponse response = result.result();
- if (!HttpStatusCodeIdentifier.isSuccessCode(response.statusCode())
- && !HttpStatusCodeIdentifier.isRedirectCode(response.statusCode())) {
- System.err.println("HTTP " + response.statusCode() + ": " + response.statusMessage());
- future.completeExceptionally(
- new HttpException(response.statusCode(), response.statusMessage()));
- return false;
- }
- return true;
- }
-
- public static CompletableFuture downloadFile(String url, String name, String suffix) {
- return fireDownloadRequest(
- webClient.get(url), name, suffix, MultiMap.caseInsensitiveMultiMap());
- }
-
- public static CompletableFuture downloadFile(
- String url, String name, String suffix, MultiMap headers) {
- return fireDownloadRequest(webClient.get(url), name, suffix, headers);
- }
-
- public static CompletableFuture downloadFileAbs(String url, String name, String suffix) {
- return fireDownloadRequest(
- webClient.getAbs(url), name, suffix, MultiMap.caseInsensitiveMultiMap());
- }
-
- public static CompletableFuture downloadFileAbs(
- String url, String name, String suffix, MultiMap headers) {
- return fireDownloadRequest(webClient.getAbs(url), name, suffix, headers);
- }
-
- private static CompletableFuture fireDownloadRequest(
- HttpRequest request, String filename, String fileSuffix, MultiMap headers) {
- CompletableFuture future = new CompletableFuture<>();
- WORKER.submit(
- () -> {
- request.putHeaders(headers)
- .followRedirects(true)
- .send(
- ar -> {
- if (ar.failed()) {
- future.completeExceptionally(ar.cause());
- return;
- }
- HttpResponse resp = ar.result();
- logger.tracev(
- "GET {0} -> HTTP {1} {2}: [{3}]",
- request.uri(),
- resp.statusCode(),
- resp.statusMessage(),
- resp.headers());
- if (!(HttpStatusCodeIdentifier.isSuccessCode(
- resp.statusCode()))) {
- future.completeExceptionally(
- new Exception(
- String.format(
- "HTTP %d", resp.statusCode())));
- return;
- }
- FileSystem fs = Utils.getFileSystem();
- String file =
- fs.createTempFileBlocking(filename, fileSuffix);
- fs.writeFileBlocking(file, ar.result().body());
- future.complete(Paths.get(file));
- });
- });
- return future;
- }
}