diff --git a/pom.xml b/pom.xml
index 5eaa5acad..e99c9d6b3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,6 +55,7 @@
2
3.2.3
${surefire.rerunFailingTestsCount}
+ 2.10.1
@@ -229,6 +230,12 @@
${com.github.spotbugs.version}
provided
+
+ com.google.code.gson
+ gson
+ ${com.google.code.gson.version}
+ test
+
io.rest-assured
rest-assured
diff --git a/smoketest/compose/sample-apps.yml b/smoketest/compose/sample-apps.yml
index 90fc24e18..4f93679f6 100644
--- a/smoketest/compose/sample-apps.yml
+++ b/smoketest/compose/sample-apps.yml
@@ -1,6 +1,6 @@
version: "3"
services:
- sample-app-1:
+ sample-app:
depends_on:
cryostat:
condition: service_healthy
@@ -12,9 +12,9 @@ services:
CRYOSTAT_AGENT_APP_NAME: "vertx-fib-demo-1"
CRYOSTAT_AGENT_WEBCLIENT_SSL_TRUST_ALL: "true"
CRYOSTAT_AGENT_WEBCLIENT_SSL_VERIFY_HOSTNAME: "false"
- CRYOSTAT_AGENT_WEBSERVER_HOST: "sample-app-1"
+ CRYOSTAT_AGENT_WEBSERVER_HOST: "sample-app"
CRYOSTAT_AGENT_WEBSERVER_PORT: "8910"
- CRYOSTAT_AGENT_CALLBACK: "http://sample-app-1:8910/"
+ CRYOSTAT_AGENT_CALLBACK: "http://sample-app:8910/"
CRYOSTAT_AGENT_BASEURI: "http://cryostat:8181/"
CRYOSTAT_AGENT_TRUST_ALL: "true"
CRYOSTAT_AGENT_AUTHORIZATION: "Basic dXNlcjpwYXNz"
@@ -22,7 +22,7 @@ services:
- "8081:8081"
labels:
io.cryostat.discovery: "true"
- io.cryostat.jmxHost: "sample-app-1"
+ io.cryostat.jmxHost: "sample-app"
io.cryostat.jmxPort: "9093"
restart: always
healthcheck:
@@ -31,63 +31,6 @@ services:
retries: 3
start_period: 10s
timeout: 5s
- sample-app-2:
- depends_on:
- cryostat:
- condition: service_healthy
- image: quay.io/andrewazores/vertx-fib-demo:0.13.0
- hostname: vertx-fib-demo-2
- environment:
- HTTP_PORT: 8082
- JMX_PORT: 9094
- USE_AUTH: "true"
- CRYOSTAT_AGENT_APP_NAME: "vertx-fib-demo-2"
- CRYOSTAT_AGENT_WEBCLIENT_SSL_TRUST_ALL: "true"
- CRYOSTAT_AGENT_WEBCLIENT_SSL_VERIFY_HOSTNAME: "false"
- CRYOSTAT_AGENT_WEBSERVER_HOST: "sample-app-2"
- CRYOSTAT_AGENT_WEBSERVER_PORT: "8911"
- CRYOSTAT_AGENT_CALLBACK: "http://sample-app-2:8911/"
- CRYOSTAT_AGENT_BASEURI: "http://cryostat:8181/"
- CRYOSTAT_AGENT_TRUST_ALL: "true"
- CRYOSTAT_AGENT_AUTHORIZATION: "Basic dXNlcjpwYXNz"
- ports:
- - "8082:8082"
- restart: always
- healthcheck:
- test: curl --fail http://localhost:8081 || exit 1
- interval: 10s
- retries: 3
- start_period: 10s
- timeout: 5s
- sample-app-3:
- depends_on:
- cryostat:
- condition: service_healthy
- image: quay.io/andrewazores/vertx-fib-demo:0.13.0
- hostname: vertx-fib-demo-3
- environment:
- HTTP_PORT: 8083
- JMX_PORT: 9095
- USE_AUTH: "true"
- USE_SSL: "true"
- CRYOSTAT_AGENT_APP_NAME: "vertx-fib-demo-3"
- CRYOSTAT_AGENT_WEBCLIENT_SSL_TRUST_ALL: "true"
- CRYOSTAT_AGENT_WEBCLIENT_SSL_VERIFY_HOSTNAME: "false"
- CRYOSTAT_AGENT_WEBSERVER_HOST: "sample-app-3"
- CRYOSTAT_AGENT_WEBSERVER_PORT: "8910"
- CRYOSTAT_AGENT_CALLBACK: "http://sample-app-3:8912/"
- CRYOSTAT_AGENT_BASEURI: "http://cryostat:8181/"
- CRYOSTAT_AGENT_TRUST_ALL: "true"
- CRYOSTAT_AGENT_AUTHORIZATION: "Basic dXNlcjpwYXNz"
- ports:
- - "8083:8083"
- restart: always
- healthcheck:
- test: curl --fail http://localhost:8081 || exit 1
- interval: 10s
- retries: 3
- start_period: 10s
- timeout: 5s
quarkus-test-agent:
image: quay.io/andrewazores/quarkus-test:latest
# do not add a depends_on:cryostat here, so that we can test that the agent is tolerant of that state
@@ -97,7 +40,7 @@ services:
expose:
- "9977"
environment:
- JAVA_OPTS_APPEND: "-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -javaagent:/deployments/app/cryostat-agent.jar"
+ JAVA_OPTS: "-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -javaagent:/deployments/app/cryostat-agent.jar"
QUARKUS_HTTP_PORT: 10010
ORG_ACME_CRYOSTATSERVICE_ENABLED: "false"
CRYOSTAT_AGENT_APP_NAME: quarkus-test-agent
diff --git a/smoketest/k8s/sample-app-2-deployment.yaml b/smoketest/k8s/sample-app-2-deployment.yaml
deleted file mode 100644
index efc63c48a..000000000
--- a/smoketest/k8s/sample-app-2-deployment.yaml
+++ /dev/null
@@ -1,64 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- creationTimestamp: null
- labels:
- io.kompose.service: sample-app-2
- name: sample-app-2
-spec:
- replicas: 1
- selector:
- matchLabels:
- io.kompose.service: sample-app-2
- strategy: {}
- template:
- metadata:
- creationTimestamp: null
- labels:
- io.kompose.network/compose-default: "true"
- io.kompose.service: sample-app-2
- spec:
- containers:
- - env:
- - name: CRYOSTAT_AGENT_APP_NAME
- value: vertx-fib-demo-2
- - name: CRYOSTAT_AGENT_AUTHORIZATION
- value: Basic dXNlcjpwYXNz
- - name: CRYOSTAT_AGENT_BASEURI
- value: http://cryostat:8181/
- - name: CRYOSTAT_AGENT_CALLBACK
- value: http://sample-app-2:8911/
- - name: CRYOSTAT_AGENT_TRUST_ALL
- value: "true"
- - name: CRYOSTAT_AGENT_WEBCLIENT_SSL_TRUST_ALL
- value: "true"
- - name: CRYOSTAT_AGENT_WEBCLIENT_SSL_VERIFY_HOSTNAME
- value: "false"
- - name: CRYOSTAT_AGENT_WEBSERVER_HOST
- value: sample-app-2
- - name: CRYOSTAT_AGENT_WEBSERVER_PORT
- value: "8911"
- - name: HTTP_PORT
- value: "8082"
- - name: JMX_PORT
- value: "9094"
- - name: USE_AUTH
- value: "true"
- image: quay.io/andrewazores/vertx-fib-demo:0.13.0
- livenessProbe:
- exec:
- command:
- - curl --fail http://localhost:8081 || exit 1
- failureThreshold: 3
- initialDelaySeconds: 10
- periodSeconds: 10
- timeoutSeconds: 5
- name: sample-app-2
- ports:
- - containerPort: 8082
- hostPort: 8082
- protocol: TCP
- resources: {}
- hostname: vertx-fib-demo-2
- restartPolicy: Always
-status: {}
diff --git a/smoketest/k8s/sample-app-2-service.yaml b/smoketest/k8s/sample-app-2-service.yaml
deleted file mode 100644
index c9f5e71b9..000000000
--- a/smoketest/k8s/sample-app-2-service.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- creationTimestamp: null
- labels:
- io.kompose.service: sample-app-2
- name: sample-app-2
-spec:
- ports:
- - name: "8082"
- port: 8082
- targetPort: 8082
- selector:
- io.kompose.service: sample-app-2
-status:
- loadBalancer: {}
diff --git a/smoketest/k8s/sample-app-3-deployment.yaml b/smoketest/k8s/sample-app-3-deployment.yaml
deleted file mode 100644
index c7bceaddd..000000000
--- a/smoketest/k8s/sample-app-3-deployment.yaml
+++ /dev/null
@@ -1,66 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- creationTimestamp: null
- labels:
- io.kompose.service: sample-app-3
- name: sample-app-3
-spec:
- replicas: 1
- selector:
- matchLabels:
- io.kompose.service: sample-app-3
- strategy: {}
- template:
- metadata:
- creationTimestamp: null
- labels:
- io.kompose.network/compose-default: "true"
- io.kompose.service: sample-app-3
- spec:
- containers:
- - env:
- - name: CRYOSTAT_AGENT_APP_NAME
- value: vertx-fib-demo-3
- - name: CRYOSTAT_AGENT_AUTHORIZATION
- value: Basic dXNlcjpwYXNz
- - name: CRYOSTAT_AGENT_BASEURI
- value: http://cryostat:8181/
- - name: CRYOSTAT_AGENT_CALLBACK
- value: http://sample-app-3:8912/
- - name: CRYOSTAT_AGENT_TRUST_ALL
- value: "true"
- - name: CRYOSTAT_AGENT_WEBCLIENT_SSL_TRUST_ALL
- value: "true"
- - name: CRYOSTAT_AGENT_WEBCLIENT_SSL_VERIFY_HOSTNAME
- value: "false"
- - name: CRYOSTAT_AGENT_WEBSERVER_HOST
- value: sample-app-3
- - name: CRYOSTAT_AGENT_WEBSERVER_PORT
- value: "8910"
- - name: HTTP_PORT
- value: "8083"
- - name: JMX_PORT
- value: "9095"
- - name: USE_AUTH
- value: "true"
- - name: USE_SSL
- value: "true"
- image: quay.io/andrewazores/vertx-fib-demo:0.13.0
- livenessProbe:
- exec:
- command:
- - curl --fail http://localhost:8081 || exit 1
- failureThreshold: 3
- initialDelaySeconds: 10
- periodSeconds: 10
- timeoutSeconds: 5
- name: sample-app-3
- ports:
- - containerPort: 8083
- hostPort: 8083
- protocol: TCP
- resources: {}
- hostname: vertx-fib-demo-3
- restartPolicy: Always
-status: {}
diff --git a/smoketest/k8s/sample-app-3-service.yaml b/smoketest/k8s/sample-app-3-service.yaml
deleted file mode 100644
index a27eeca2e..000000000
--- a/smoketest/k8s/sample-app-3-service.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- creationTimestamp: null
- labels:
- io.kompose.service: sample-app-3
- name: sample-app-3
-spec:
- ports:
- - name: "8083"
- port: 8083
- targetPort: 8083
- selector:
- io.kompose.service: sample-app-3
-status:
- loadBalancer: {}
diff --git a/smoketest/k8s/sample-app-1-deployment.yaml b/smoketest/k8s/sample-app-deployment.yaml
similarity index 84%
rename from smoketest/k8s/sample-app-1-deployment.yaml
rename to smoketest/k8s/sample-app-deployment.yaml
index 489a46665..53c2baf3c 100644
--- a/smoketest/k8s/sample-app-1-deployment.yaml
+++ b/smoketest/k8s/sample-app-deployment.yaml
@@ -3,28 +3,28 @@ kind: Deployment
metadata:
annotations:
io.cryostat.discovery: "true"
- io.cryostat.jmxHost: sample-app-1
+ io.cryostat.jmxHost: sample-app
io.cryostat.jmxPort: "9093"
creationTimestamp: null
labels:
- io.kompose.service: sample-app-1
- name: sample-app-1
+ io.kompose.service: sample-app
+ name: sample-app
spec:
replicas: 1
selector:
matchLabels:
- io.kompose.service: sample-app-1
+ io.kompose.service: sample-app
strategy: {}
template:
metadata:
annotations:
io.cryostat.discovery: "true"
- io.cryostat.jmxHost: sample-app-1
+ io.cryostat.jmxHost: sample-app
io.cryostat.jmxPort: "9093"
creationTimestamp: null
labels:
io.kompose.network/compose-default: "true"
- io.kompose.service: sample-app-1
+ io.kompose.service: sample-app
spec:
containers:
- env:
@@ -35,7 +35,7 @@ spec:
- name: CRYOSTAT_AGENT_BASEURI
value: http://cryostat:8181/
- name: CRYOSTAT_AGENT_CALLBACK
- value: http://sample-app-1:8910/
+ value: http://sample-app:8910/
- name: CRYOSTAT_AGENT_TRUST_ALL
value: "true"
- name: CRYOSTAT_AGENT_WEBCLIENT_SSL_TRUST_ALL
@@ -43,7 +43,7 @@ spec:
- name: CRYOSTAT_AGENT_WEBCLIENT_SSL_VERIFY_HOSTNAME
value: "false"
- name: CRYOSTAT_AGENT_WEBSERVER_HOST
- value: sample-app-1
+ value: sample-app
- name: CRYOSTAT_AGENT_WEBSERVER_PORT
value: "8910"
- name: HTTP_PORT
@@ -59,7 +59,7 @@ spec:
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
- name: sample-app-1
+ name: sample-app
ports:
- containerPort: 8081
hostPort: 8081
diff --git a/smoketest/k8s/sample-app-1-service.yaml b/smoketest/k8s/sample-app-service.yaml
similarity index 66%
rename from smoketest/k8s/sample-app-1-service.yaml
rename to smoketest/k8s/sample-app-service.yaml
index f9c56883d..323741f45 100644
--- a/smoketest/k8s/sample-app-1-service.yaml
+++ b/smoketest/k8s/sample-app-service.yaml
@@ -3,18 +3,18 @@ kind: Service
metadata:
annotations:
io.cryostat.discovery: "true"
- io.cryostat.jmxHost: sample-app-1
+ io.cryostat.jmxHost: sample-app
io.cryostat.jmxPort: "9093"
creationTimestamp: null
labels:
- io.kompose.service: sample-app-1
- name: sample-app-1
+ io.kompose.service: sample-app
+ name: sample-app
spec:
ports:
- name: "8081"
port: 8081
targetPort: 8081
selector:
- io.kompose.service: sample-app-1
+ io.kompose.service: sample-app
status:
loadBalancer: {}
diff --git a/src/main/java/io/cryostat/ExceptionMappers.java b/src/main/java/io/cryostat/ExceptionMappers.java
index f97c58b2b..f03dc89fd 100644
--- a/src/main/java/io/cryostat/ExceptionMappers.java
+++ b/src/main/java/io/cryostat/ExceptionMappers.java
@@ -15,10 +15,6 @@
*/
package io.cryostat;
-import org.openjdk.jmc.rjmx.ConnectionException;
-
-import io.cryostat.targets.TargetConnectionManager;
-
import io.netty.handler.codec.http.HttpResponseStatus;
import jakarta.persistence.NoResultException;
import org.hibernate.exception.ConstraintViolationException;
@@ -57,18 +53,4 @@ public RestResponse mapNoSuchKeyException(NoSuchKeyException ex) {
public RestResponse mapIllegalArgumentException(IllegalArgumentException exception) {
return RestResponse.status(HttpResponseStatus.BAD_REQUEST.code());
}
-
- @ServerExceptionMapper
- public RestResponse mapJmxConnectionException(ConnectionException exception) {
- return RestResponse.status(HttpResponseStatus.BAD_GATEWAY.code());
- }
-
- @ServerExceptionMapper
- public RestResponse mapFlightRecorderException(
- org.openjdk.jmc.rjmx.services.jfr.FlightRecorderException exception) {
- if (TargetConnectionManager.isJmxAuthFailure(exception)) {
- return RestResponse.status(HttpResponseStatus.FORBIDDEN.code());
- }
- return RestResponse.status(HttpResponseStatus.BAD_GATEWAY.code());
- }
}
diff --git a/src/main/java/io/cryostat/V2Response.java b/src/main/java/io/cryostat/V2Response.java
index 6f0ccfd4f..f5db1c711 100644
--- a/src/main/java/io/cryostat/V2Response.java
+++ b/src/main/java/io/cryostat/V2Response.java
@@ -18,18 +18,10 @@
import java.util.Objects;
import jakarta.annotation.Nullable;
-import jakarta.ws.rs.core.Response;
public record V2Response(Meta meta, Data data) {
- public static V2Response json(Response.Status status, Object payload) {
- Data data;
- if (status.getFamily().equals(Response.Status.Family.CLIENT_ERROR)
- || status.getFamily().equals(Response.Status.Family.SERVER_ERROR)) {
- data = new ErrorData(payload);
- } else {
- data = new PayloadData(payload);
- }
- return new V2Response(new Meta("application/json", status.getReasonPhrase()), data);
+ public static V2Response json(Object payload, String status) {
+ return new V2Response(new Meta("application/json", status), new Data(payload));
}
// FIXME the type and status should both come from an enum and be non-null
@@ -40,33 +32,5 @@ public record Meta(String type, String status) {
}
}
- interface Data {}
-
- public static class PayloadData implements Data {
- @Nullable Object result;
-
- public PayloadData(Object payload) {
- this.result = payload;
- }
-
- public Object getResult() {
- return result;
- }
- }
-
- public static class ErrorData implements Data {
- String reason;
-
- public ErrorData(Object payload) {
- if (payload instanceof Exception) {
- this.reason = ((Exception) Objects.requireNonNull(payload)).getMessage();
- } else {
- this.reason = Objects.requireNonNull(payload).toString();
- }
- }
-
- public String getReason() {
- return reason;
- }
- }
+ public record Data(@Nullable Object result) {}
}
diff --git a/src/main/java/io/cryostat/credentials/Credential.java b/src/main/java/io/cryostat/credentials/Credential.java
index af0d4f9a7..00478f806 100644
--- a/src/main/java/io/cryostat/credentials/Credential.java
+++ b/src/main/java/io/cryostat/credentials/Credential.java
@@ -40,10 +40,6 @@
@EntityListeners(Credential.Listener.class)
public class Credential extends PanacheEntity {
- public static final String CREDENTIALS_STORED = "CredentialsStored";
- public static final String CREDENTIALS_DELETED = "CredentialsDeleted";
- public static final String CREDENTIALS_UPDATED = "CredentialsUpdated";
-
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "matchExpression")
public MatchExpression matchExpression;
@@ -62,6 +58,7 @@ public class Credential extends PanacheEntity {
@ApplicationScoped
static class Listener {
+
@Inject EventBus bus;
@Inject MatchExpression.TargetMatcher targetMatcher;
@@ -70,8 +67,7 @@ public void postPersist(Credential credential) throws ScriptException {
bus.publish(
MessagingServer.class.getName(),
new Notification(
- CREDENTIALS_STORED, Credentials.notificationResult(credential)));
- bus.publish(CREDENTIALS_STORED, credential);
+ "CredentialsStored", Credentials.notificationResult(credential)));
}
@PostUpdate
@@ -79,8 +75,7 @@ public void postUpdate(Credential credential) throws ScriptException {
bus.publish(
MessagingServer.class.getName(),
new Notification(
- CREDENTIALS_UPDATED, Credentials.notificationResult(credential)));
- bus.publish(CREDENTIALS_UPDATED, credential);
+ "CredentialsUpdated", Credentials.notificationResult(credential)));
}
@PostRemove
@@ -88,8 +83,7 @@ public void postRemove(Credential credential) throws ScriptException {
bus.publish(
MessagingServer.class.getName(),
new Notification(
- CREDENTIALS_DELETED, Credentials.notificationResult(credential)));
- bus.publish(CREDENTIALS_DELETED, credential);
+ "CredentialsDeleted", Credentials.notificationResult(credential)));
}
}
}
diff --git a/src/main/java/io/cryostat/credentials/Credentials.java b/src/main/java/io/cryostat/credentials/Credentials.java
index 2feffcf45..0234ca5a6 100644
--- a/src/main/java/io/cryostat/credentials/Credentials.java
+++ b/src/main/java/io/cryostat/credentials/Credentials.java
@@ -33,12 +33,12 @@
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.Response;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestPath;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
+import org.jboss.resteasy.reactive.RestResponse.Status;
import org.projectnessie.cel.tools.ScriptException;
@Path("/api/v2.2/credentials")
@@ -52,7 +52,6 @@ public class Credentials {
public V2Response list() {
List credentials = Credential.listAll();
return V2Response.json(
- Response.Status.OK,
credentials.stream()
.map(
c -> {
@@ -64,7 +63,8 @@ public V2Response list() {
}
})
.filter(Objects::nonNull)
- .toList());
+ .toList(),
+ Status.OK.toString());
}
@GET
@@ -72,7 +72,7 @@ public V2Response list() {
@Path("/{id}")
public V2Response get(@RestPath long id) throws ScriptException {
Credential credential = Credential.find("id", id).singleResult();
- return V2Response.json(Response.Status.OK, safeMatchedResult(credential, targetMatcher));
+ return V2Response.json(safeMatchedResult(credential, targetMatcher), Status.OK.toString());
}
@Transactional
diff --git a/src/main/java/io/cryostat/discovery/CustomDiscovery.java b/src/main/java/io/cryostat/discovery/CustomDiscovery.java
index 12cb7bc4a..59f51b86f 100644
--- a/src/main/java/io/cryostat/discovery/CustomDiscovery.java
+++ b/src/main/java/io/cryostat/discovery/CustomDiscovery.java
@@ -20,20 +20,15 @@
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Map;
-import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import io.cryostat.V2Response;
-import io.cryostat.credentials.Credential;
-import io.cryostat.expressions.MatchExpression;
import io.cryostat.targets.JvmIdException;
import io.cryostat.targets.Target;
import io.cryostat.targets.Target.Annotations;
import io.cryostat.targets.TargetConnectionManager;
import io.quarkus.runtime.StartupEvent;
-import io.smallrye.common.annotation.Blocking;
import io.vertx.mutiny.core.eventbus.EventBus;
import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.ApplicationScoped;
@@ -44,9 +39,7 @@
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
-import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.jboss.logging.Logger;
@@ -84,73 +77,29 @@ void onStart(@Observes StartupEvent evt) {
@Transactional(rollbackOn = {JvmIdException.class})
@POST
@Path("/api/v2/targets")
- @Consumes(MediaType.APPLICATION_JSON)
+ @Consumes("application/json")
@RolesAllowed("write")
- public Response create(
- Target target, @RestQuery boolean dryrun, @RestQuery boolean storeCredentials) {
- // TODO handle credentials embedded in JSON body
- return doV2Create(target, Optional.empty(), dryrun, storeCredentials);
- }
-
- @Transactional
- @POST
- @Path("/api/v2/targets")
- @Consumes({MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED})
- @RolesAllowed("write")
- public Response create(
- @RestForm URI connectUrl,
- @RestForm String alias,
- @RestForm String username,
- @RestForm String password,
- @RestQuery boolean dryrun,
- @RestQuery boolean storeCredentials) {
- var target = new Target();
- target.connectUrl = connectUrl;
- target.alias = alias;
-
- Credential credential = null;
- if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
- credential = new Credential();
- credential.matchExpression =
- new MatchExpression(
- String.format("target.connectUrl == \"%s\"", connectUrl.toString()));
- credential.username = username;
- credential.password = password;
- }
-
- return doV2Create(target, Optional.ofNullable(credential), dryrun, storeCredentials);
- }
-
- @Transactional
- @Blocking
- Response doV2Create(
- Target target,
- Optional credential,
- boolean dryrun,
- boolean storeCredentials) {
+ public Response create(Target target, @RestQuery boolean dryrun) {
try {
target.connectUrl = sanitizeConnectUrl(target.connectUrl.toString());
try {
- target.jvmId =
- connectionManager.executeDirect(
- target, credential, conn -> conn.getJvmId());
+ if (target.isAgent()) {
+ // TODO test connection
+ target.jvmId = target.connectUrl.toString();
+ } else {
+ target.jvmId =
+ connectionManager.executeConnectedTask(target, conn -> conn.getJvmId());
+ }
} catch (Exception e) {
logger.error("Target connection failed", e);
- return Response.status(Response.Status.BAD_REQUEST.getStatusCode())
- .entity(V2Response.json(Response.Status.BAD_REQUEST, e))
- .build();
+ return Response.status(400).build();
}
if (dryrun) {
- return Response.accepted()
- .entity(V2Response.json(Response.Status.ACCEPTED, target))
- .build();
+ return Response.ok().build();
}
- target.persist();
- credential.ifPresent(c -> c.persist());
-
target.activeRecordings = new ArrayList<>();
target.labels = Map.of();
target.annotations = new Annotations();
@@ -165,23 +114,31 @@ Response doV2Create(
node.persist();
realm.persist();
- return Response.created(URI.create("/api/v3/targets/" + target.id))
- .entity(V2Response.json(Response.Status.CREATED, target))
- .build();
+ return Response.created(URI.create("/api/v3/targets/" + target.id)).build();
} catch (Exception e) {
if (ExceptionUtils.indexOfType(e, ConstraintViolationException.class) >= 0) {
logger.warn("Invalid target definition", e);
- return Response.status(Response.Status.BAD_REQUEST.getStatusCode())
- .entity(V2Response.json(Response.Status.BAD_REQUEST, e))
- .build();
+ return Response.status(400).build();
}
logger.error("Unknown error", e);
- return Response.serverError()
- .entity(V2Response.json(Response.Status.INTERNAL_SERVER_ERROR, e))
- .build();
+ return Response.serverError().build();
}
}
+ @Transactional
+ @POST
+ @Path("/api/v2/targets")
+ @Consumes("multipart/form-data")
+ @RolesAllowed("write")
+ public Response create(
+ @RestForm URI connectUrl, @RestForm String alias, @RestQuery boolean dryrun) {
+ var target = new Target();
+ target.connectUrl = connectUrl;
+ target.alias = alias;
+
+ return create(target, dryrun);
+ }
+
@Transactional
@DELETE
@Path("/api/v2/targets/{connectUrl}")
@@ -201,9 +158,9 @@ public Response delete(@RestPath long id) throws URISyntaxException {
Target target = Target.find("id", id).singleResult();
DiscoveryNode realm = DiscoveryNode.getRealm(REALM).orElseThrow();
realm.children.remove(target.discoveryNode);
- realm.persist();
target.delete();
- return Response.noContent().build();
+ realm.persist();
+ return Response.ok().build();
}
private URI sanitizeConnectUrl(String in) throws URISyntaxException, MalformedURLException {
diff --git a/src/main/java/io/cryostat/discovery/Discovery.java b/src/main/java/io/cryostat/discovery/Discovery.java
index 9de683c48..e6d6b1ddd 100644
--- a/src/main/java/io/cryostat/discovery/Discovery.java
+++ b/src/main/java/io/cryostat/discovery/Discovery.java
@@ -110,7 +110,7 @@ public RestResponse checkRegistration(@RestPath UUID id, @RestQuery String
@Transactional
@POST
@Path("/api/v2.2/discovery")
- @Consumes(MediaType.APPLICATION_JSON)
+ @Consumes("application/json")
@RolesAllowed("write")
public Map register(JsonObject body) throws URISyntaxException {
String id = body.getString("id");
@@ -147,7 +147,7 @@ public Map register(JsonObject body) throws URISyntaxException {
@Transactional
@POST
@Path("/api/v2.2/discovery/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
+ @Consumes("application/json")
@PermitAll
public Map> publish(
@RestPath UUID id, @RestQuery String token, List body) {
diff --git a/src/main/java/io/cryostat/events/Events.java b/src/main/java/io/cryostat/events/Events.java
index 8ed0df82f..e524bf551 100644
--- a/src/main/java/io/cryostat/events/Events.java
+++ b/src/main/java/io/cryostat/events/Events.java
@@ -37,6 +37,7 @@
import org.jboss.resteasy.reactive.RestPath;
import org.jboss.resteasy.reactive.RestQuery;
import org.jboss.resteasy.reactive.RestResponse;
+import org.jboss.resteasy.reactive.RestResponse.Status;
@Path("")
public class Events {
@@ -64,7 +65,7 @@ public Response listEventsV1(@RestPath URI connectUrl, @RestQuery String q) thro
@RolesAllowed("read")
public V2Response listEventsV2(@RestPath URI connectUrl, @RestQuery String q) throws Exception {
return V2Response.json(
- Response.Status.OK, searchEvents(Target.getTargetByConnectUrl(connectUrl), q));
+ searchEvents(Target.getTargetByConnectUrl(connectUrl), q), Status.OK.toString());
}
@GET
diff --git a/src/main/java/io/cryostat/expressions/MatchExpressionEvaluator.java b/src/main/java/io/cryostat/expressions/MatchExpressionEvaluator.java
index e31c21055..e25a5350c 100644
--- a/src/main/java/io/cryostat/expressions/MatchExpressionEvaluator.java
+++ b/src/main/java/io/cryostat/expressions/MatchExpressionEvaluator.java
@@ -37,7 +37,6 @@
import io.quarkus.cache.CompositeCacheKey;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.common.annotation.Blocking;
-import jakarta.annotation.Nullable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jdk.jfr.Category;
@@ -199,12 +198,13 @@ public static class ScriptCreationEvent extends Event {}
private static record SimplifiedTarget(
String connectUrl,
String alias,
- @Nullable String jvmId,
+ String jvmId,
Map labels,
Target.Annotations annotations) {
SimplifiedTarget {
Objects.requireNonNull(connectUrl);
Objects.requireNonNull(alias);
+ Objects.requireNonNull(jvmId);
if (labels == null) {
labels = Collections.emptyMap();
}
diff --git a/src/main/java/io/cryostat/expressions/MatchExpressions.java b/src/main/java/io/cryostat/expressions/MatchExpressions.java
index 256413761..80ce6bb09 100644
--- a/src/main/java/io/cryostat/expressions/MatchExpressions.java
+++ b/src/main/java/io/cryostat/expressions/MatchExpressions.java
@@ -32,7 +32,7 @@
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestPath;
import org.projectnessie.cel.tools.ScriptException;
@@ -53,7 +53,7 @@ public V2Response test(RequestData requestData) throws ScriptException {
var matched =
targetMatcher.match(
new MatchExpression(requestData.matchExpression), requestData.targets);
- return V2Response.json(Response.Status.OK, matched);
+ return V2Response.json(matched, Status.OK.toString());
}
@GET
diff --git a/src/main/java/io/cryostat/recordings/Recordings.java b/src/main/java/io/cryostat/recordings/Recordings.java
index d76335fb4..ae45f6e9a 100644
--- a/src/main/java/io/cryostat/recordings/Recordings.java
+++ b/src/main/java/io/cryostat/recordings/Recordings.java
@@ -536,12 +536,12 @@ public Response createSnapshotV2(@RestPath URI connectUrl) throws Exception {
return Response.status(Response.Status.CREATED)
.entity(
V2Response.json(
- Response.Status.CREATED,
- recordingHelper.toExternalForm(recording)))
+ recordingHelper.toExternalForm(recording),
+ RestResponse.Status.CREATED.toString()))
.build();
} catch (SnapshotCreationException sce) {
return Response.status(Response.Status.ACCEPTED)
- .entity(V2Response.json(Response.Status.ACCEPTED, null))
+ .entity(V2Response.json(null, RestResponse.Status.ACCEPTED.toString()))
.build();
}
}
diff --git a/src/main/java/io/cryostat/rules/Rules.java b/src/main/java/io/cryostat/rules/Rules.java
index 754ce4cc7..70f7e0e39 100644
--- a/src/main/java/io/cryostat/rules/Rules.java
+++ b/src/main/java/io/cryostat/rules/Rules.java
@@ -39,6 +39,7 @@
import org.jboss.resteasy.reactive.RestQuery;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
+import org.jboss.resteasy.reactive.RestResponse.Status;
@Path("/api/v2/rules")
public class Rules {
@@ -48,20 +49,20 @@ public class Rules {
@GET
@RolesAllowed("read")
public RestResponse list() {
- return RestResponse.ok(V2Response.json(Response.Status.OK, Rule.listAll()));
+ return RestResponse.ok(V2Response.json(Rule.listAll(), Status.OK.getReasonPhrase()));
}
@GET
@RolesAllowed("read")
@Path("/{name}")
public RestResponse get(@RestPath String name) {
- return RestResponse.ok(V2Response.json(Response.Status.OK, Rule.getByName(name)));
+ return RestResponse.ok(V2Response.json(Rule.getByName(name), Status.OK.getReasonPhrase()));
}
@Transactional
@POST
@RolesAllowed("write")
- @Consumes(MediaType.APPLICATION_JSON)
+ @Consumes({MediaType.APPLICATION_JSON})
public RestResponse create(Rule rule) {
// TODO validate the incoming rule
if (rule == null) {
@@ -74,7 +75,7 @@ public RestResponse create(Rule rule) {
rule.persist();
return ResponseBuilder.create(
Response.Status.CREATED,
- V2Response.json(Response.Status.CREATED, rule.name))
+ V2Response.json(rule.name, Status.CREATED.toString()))
.build();
}
@@ -82,7 +83,7 @@ public RestResponse create(Rule rule) {
@PATCH
@RolesAllowed("write")
@Path("/{name}")
- @Consumes(MediaType.APPLICATION_JSON)
+ @Consumes("application/json")
public RestResponse update(
@RestPath String name, @RestQuery boolean clean, JsonObject body) {
Rule rule = Rule.getByName(name);
@@ -94,7 +95,7 @@ public RestResponse update(
rule.enabled = enabled;
rule.persist();
- return ResponseBuilder.ok(V2Response.json(Response.Status.OK, rule)).build();
+ return ResponseBuilder.ok(V2Response.json(rule, Status.OK.toString())).build();
}
@Transactional
@@ -141,7 +142,7 @@ public RestResponse delete(@RestPath String name, @RestQuery boolean
bus.send(Rule.RULE_ADDRESS + "?clean", rule);
}
rule.delete();
- return RestResponse.ok(V2Response.json(Response.Status.OK, null));
+ return RestResponse.ok(V2Response.json(null, Status.OK.toString()));
}
static class RuleExistsException extends ClientErrorException {
diff --git a/src/main/java/io/cryostat/security/Auth.java b/src/main/java/io/cryostat/security/Auth.java
index e7553937a..8791dbee0 100644
--- a/src/main/java/io/cryostat/security/Auth.java
+++ b/src/main/java/io/cryostat/security/Auth.java
@@ -27,7 +27,6 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
-import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.jboss.logging.Logger;
@@ -39,7 +38,7 @@ public class Auth {
@POST
@Path("/api/v2.1/logout")
@PermitAll
- @Produces(MediaType.APPLICATION_JSON)
+ @Produces("application/json")
public Response logout(@Context RoutingContext context) {
HttpAuthenticator authenticator = context.get(HttpAuthenticator.class.getName());
return authenticator
@@ -63,7 +62,7 @@ public Response logout(@Context RoutingContext context) {
@POST
@Path("/api/v2.1/auth")
@PermitAll
- @Produces(MediaType.APPLICATION_JSON)
+ @Produces("application/json")
public Response login(@Context RoutingContext context) {
HttpAuthenticator authenticator = context.get(HttpAuthenticator.class.getName());
return authenticator
diff --git a/src/main/java/io/cryostat/targets/Target.java b/src/main/java/io/cryostat/targets/Target.java
index bd8cf7f1d..50dad8780 100644
--- a/src/main/java/io/cryostat/targets/Target.java
+++ b/src/main/java/io/cryostat/targets/Target.java
@@ -233,9 +233,11 @@ void prePersist(Target target) throws JvmIdException {
connectionManager.executeConnectedTask(target, conn -> conn.getJvmId());
} catch (Exception e) {
// TODO tolerate this in the condition that the connection failed because of JMX
- // auth. In that instance then persist the entity with a null jvmId, but listen for
- // new Credentials and test them against any targets with null jvmIds to see if we
- // can populate them.
+ // auth.
+ // In that instance then persist the entity with a null jvmId, but listen for new
+ // Credentials
+ // and test them against any targets with null jvmIds to see if we can populate
+ // them.
throw new JvmIdException(e);
}
}
diff --git a/src/main/java/io/cryostat/targets/TargetConnectionManager.java b/src/main/java/io/cryostat/targets/TargetConnectionManager.java
index 09e1b97e4..6185bc74c 100644
--- a/src/main/java/io/cryostat/targets/TargetConnectionManager.java
+++ b/src/main/java/io/cryostat/targets/TargetConnectionManager.java
@@ -15,9 +15,7 @@
*/
package io.cryostat.targets;
-import java.net.SocketTimeoutException;
import java.net.URI;
-import java.rmi.ConnectIOException;
import java.time.Duration;
import java.util.Collections;
import java.util.Map;
@@ -31,16 +29,9 @@
import java.util.concurrent.Semaphore;
import javax.management.remote.JMXServiceURL;
-import javax.naming.ServiceUnavailableException;
-import javax.security.sasl.SaslException;
-
-import org.openjdk.jmc.rjmx.ConnectionException;
-import org.openjdk.jmc.rjmx.services.jfr.FlightRecorderException;
import io.cryostat.core.net.JFRConnection;
import io.cryostat.core.net.JFRConnectionToolkit;
-import io.cryostat.credentials.Credential;
-import io.cryostat.expressions.MatchExpressionEvaluator;
import io.cryostat.targets.Target.EventKind;
import io.cryostat.targets.Target.TargetDiscovery;
@@ -54,21 +45,17 @@
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
-import jakarta.transaction.Transactional;
import jdk.jfr.Category;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Label;
import jdk.jfr.Name;
-import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jboss.logging.Logger;
-import org.projectnessie.cel.tools.ScriptException;
@ApplicationScoped
public class TargetConnectionManager {
private final JFRConnectionToolkit jfrConnectionToolkit;
- private final MatchExpressionEvaluator matchExpressionEvaluator;
private final AgentConnectionFactory agentConnectionFactory;
private final Executor executor;
private final Logger logger;
@@ -80,14 +67,12 @@ public class TargetConnectionManager {
@Inject
TargetConnectionManager(
JFRConnectionToolkit jfrConnectionToolkit,
- MatchExpressionEvaluator matchExpressionEvaluator,
AgentConnectionFactory agentConnectionFactory,
Executor executor,
Logger logger) {
FlightRecorder.register(TargetConnectionOpened.class);
FlightRecorder.register(TargetConnectionClosed.class);
this.jfrConnectionToolkit = jfrConnectionToolkit;
- this.matchExpressionEvaluator = matchExpressionEvaluator;
this.agentConnectionFactory = agentConnectionFactory;
this.executor = executor;
@@ -131,42 +116,6 @@ void onMessage(TargetDiscovery event) {
}
}
- @Blocking
- @ConsumeEvent(Credential.CREDENTIALS_STORED)
- void onCredentialsStored(Credential credential) {
- handleCredentialChange(credential);
- }
-
- @Blocking
- @ConsumeEvent(Credential.CREDENTIALS_UPDATED)
- void onCredentialsUpdated(Credential credential) {
- handleCredentialChange(credential);
- }
-
- @Blocking
- @ConsumeEvent(Credential.CREDENTIALS_DELETED)
- void onCredentialsDeleted(Credential credential) {
- handleCredentialChange(credential);
- }
-
- @Blocking
- void handleCredentialChange(Credential credential) {
- for (var entry : connections.asMap().entrySet()) {
- URI key = entry.getKey();
- var target = Target.find("connectUrl", key).firstResultOptional();
- if (target.isEmpty()) {
- continue;
- }
- try {
- if (matchExpressionEvaluator.applies(credential.matchExpression, target.get())) {
- connections.synchronous().invalidate(key);
- }
- } catch (ScriptException se) {
- logger.warn(se);
- }
- }
- }
-
public Uni executeConnectedTaskAsync(Target target, ConnectedTask task) {
return Uni.createFrom()
.completionStage(
@@ -196,15 +145,6 @@ public T executeConnectedTask(Target target, ConnectedTask task) throws E
}
}
- @Blocking
- public T executeDirect(
- Target target, Optional credentials, ConnectedTask task)
- throws Exception {
- try (var conn = connect(target.connectUrl, credentials)) {
- return task.execute(conn);
- }
- }
-
/**
* Mark a connection as still in use by the consumer. Connections expire from cache and are
* automatically closed after {@link NetworkModule.TARGET_CACHE_TTL}. For long-running
@@ -258,33 +198,7 @@ private void closeConnection(URI connectUrl, JFRConnection connection, RemovalCa
}
}
- @Transactional
- JFRConnection connect(URI connectUrl) throws Exception {
- var credentials =
- Target.find("connectUrl", connectUrl)
- .firstResultOptional()
- .map(
- t ->
- Credential.listAll().stream()
- .filter(
- c -> {
- try {
- return matchExpressionEvaluator
- .applies(
- c.matchExpression,
- t);
- } catch (ScriptException e) {
- logger.error(e);
- return false;
- }
- })
- .findFirst()
- .orElse(null));
- return connect(connectUrl, credentials);
- }
-
- @Blocking
- JFRConnection connect(URI connectUrl, Optional credentials) throws Exception {
+ private JFRConnection connect(URI connectUrl) throws Exception {
TargetConnectionOpened evt = new TargetConnectionOpened(connectUrl.toString());
evt.begin();
try {
@@ -295,14 +209,13 @@ JFRConnection connect(URI connectUrl, Optional credentials) throws E
if (Set.of("http", "https", "cryostat-agent").contains(connectUrl.getScheme())) {
return agentConnectionFactory.createConnection(connectUrl);
}
-
return jfrConnectionToolkit.connect(
new JMXServiceURL(connectUrl.toString()),
- credentials
- .map(c -> new io.cryostat.core.net.Credentials(c.username, c.password))
- .orElse(null),
+ null /* TODO get from credentials storage */,
Collections.singletonList(
- () -> connections.synchronous().invalidate(connectUrl)));
+ () -> {
+ this.connections.synchronous().invalidate(connectUrl);
+ }));
} catch (Exception e) {
evt.setExceptionThrown(true);
if (semaphore.isPresent()) {
@@ -351,33 +264,6 @@ public interface ConnectedTask {
T execute(JFRConnection connection) throws Exception;
}
- public static boolean isTargetConnectionFailure(Exception e) {
- return ExceptionUtils.indexOfType(e, ConnectionException.class) >= 0
- || ExceptionUtils.indexOfType(e, FlightRecorderException.class) >= 0;
- }
-
- public static boolean isJmxAuthFailure(Exception e) {
- return ExceptionUtils.indexOfType(e, SecurityException.class) >= 0
- || ExceptionUtils.indexOfType(e, SaslException.class) >= 0;
- }
-
- public static boolean isJmxSslFailure(Exception e) {
- return ExceptionUtils.indexOfType(e, ConnectIOException.class) >= 0
- && !isServiceTypeFailure(e);
- }
-
- /** Check if the exception happened because the port connected to a non-JMX service. */
- public static boolean isServiceTypeFailure(Exception e) {
- return ExceptionUtils.indexOfType(e, ConnectIOException.class) >= 0
- && ExceptionUtils.indexOfType(e, SocketTimeoutException.class) >= 0;
- }
-
- public static boolean isUnknownTargetFailure(Exception e) {
- return ExceptionUtils.indexOfType(e, java.net.UnknownHostException.class) >= 0
- || ExceptionUtils.indexOfType(e, java.rmi.UnknownHostException.class) >= 0
- || ExceptionUtils.indexOfType(e, ServiceUnavailableException.class) >= 0;
- }
-
@Name("io.cryostat.net.TargetConnectionManager.TargetConnectionOpened")
@Label("Target Connection Opened")
@Category("Cryostat")
diff --git a/src/main/java/io/cryostat/util/HttpStatusCodeIdentifier.java b/src/main/java/io/cryostat/util/HttpStatusCodeIdentifier.java
index 7637340fd..7efc50af2 100644
--- a/src/main/java/io/cryostat/util/HttpStatusCodeIdentifier.java
+++ b/src/main/java/io/cryostat/util/HttpStatusCodeIdentifier.java
@@ -15,29 +15,27 @@
*/
package io.cryostat.util;
-import jakarta.ws.rs.core.Response;
-
public final class HttpStatusCodeIdentifier {
private HttpStatusCodeIdentifier() {}
public static boolean isInformationCode(int code) {
- return Response.Status.Family.familyOf(code).equals(Response.Status.Family.INFORMATIONAL);
+ return 100 <= code && code < 200;
}
public static boolean isSuccessCode(int code) {
- return Response.Status.Family.familyOf(code).equals(Response.Status.Family.SUCCESSFUL);
+ return 200 <= code && code < 300;
}
public static boolean isRedirectCode(int code) {
- return Response.Status.Family.familyOf(code).equals(Response.Status.Family.REDIRECTION);
+ return 300 <= code && code < 400;
}
public static boolean isClientErrorCode(int code) {
- return Response.Status.Family.familyOf(code).equals(Response.Status.Family.CLIENT_ERROR);
+ return 400 <= code && code < 500;
}
public static boolean isServerErrorCode(int code) {
- return Response.Status.Family.familyOf(code).equals(Response.Status.Family.SERVER_ERROR);
+ return 500 <= code && code < 600;
}
}
diff --git a/src/main/java/io/cryostat/ws/MessagingServer.java b/src/main/java/io/cryostat/ws/MessagingServer.java
index 8ad65bdfa..f9e37ba34 100644
--- a/src/main/java/io/cryostat/ws/MessagingServer.java
+++ b/src/main/java/io/cryostat/ws/MessagingServer.java
@@ -16,23 +16,15 @@
package io.cryostat.ws;
import java.io.IOException;
-import java.nio.channels.ClosedChannelException;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.ConcurrentHashMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import io.quarkus.runtime.ShutdownEvent;
-import io.quarkus.runtime.StartupEvent;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.common.annotation.Blocking;
import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
@@ -40,8 +32,6 @@
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;
@ApplicationScoped
@@ -49,15 +39,9 @@
public class MessagingServer {
@Inject Logger logger;
- private final ExecutorService executor = Executors.newSingleThreadExecutor();
- private final BlockingQueue msgQ;
- private final Set sessions = new CopyOnWriteArraySet<>();
+ private final Set sessions = ConcurrentHashMap.newKeySet();
private final ObjectMapper mapper = new ObjectMapper();
- MessagingServer(@ConfigProperty(name = "cryostat.messaging.queue.size") int capacity) {
- this.msgQ = new ArrayBlockingQueue<>(capacity);
- }
-
// TODO implement authentication check
@OnOpen
public void onOpen(Session session) {
@@ -78,70 +62,42 @@ public void onError(Session session, Throwable throwable) {
logger.errorv("Closing session {0}", session.getId());
session.close();
} catch (IOException ioe) {
+ ioe.printStackTrace(System.err);
logger.error("Unable to close session", ioe);
}
}
- void start(@Observes StartupEvent evt) {
- logger.infov("Starting {0} executor", getClass().getName());
- executor.execute(
- () -> {
- while (!executor.isShutdown()) {
- try {
- var notification = msgQ.take();
- var map =
- Map.of(
- "meta",
- Map.of("category", notification.category()),
- "message",
- notification.message());
- logger.infov("Broadcasting: {0}", map);
- sessions.forEach(
- s -> {
- try {
- s.getBasicRemote()
- .sendText(mapper.writeValueAsString(map));
- } catch (JsonProcessingException e) {
- logger.error("Unable to serialize message to JSON", e);
- } catch (IOException e) {
- // ignored simple ClosedChannelExceptions since this
- // just means the connection has already been closed,
- // either due to an error or the client closing it. This
- // does not actually indicate a problem
- if (!ExceptionUtils.getThrowableList(e).stream()
- .anyMatch(
- t ->
- t
- instanceof
- ClosedChannelException)) {
- logger.errorv(
- "Unable to send message to {0}", s.getId());
- logger.error(e);
- }
- }
- });
- } catch (InterruptedException ie) {
- logger.warn(ie);
- break;
- }
- }
- });
- }
-
- void shutdown(@Observes ShutdownEvent evt) {
- logger.infov("Shutting down {0} executor", getClass().getName());
- executor.shutdown();
- msgQ.clear();
- }
-
@OnMessage
public void onMessage(Session session, String message) {
- logger.infov("{0} message: \"{1}\"", session.getId(), message);
+ logger.infov("[{0}] message: {1}", session.getId(), message);
}
@ConsumeEvent
@Blocking
void broadcast(Notification notification) {
- msgQ.add(notification);
+ var map =
+ Map.of(
+ "meta",
+ Map.of("category", notification.category()),
+ "message",
+ notification.message());
+ logger.infov("Broadcasting: {0}", map);
+ sessions.forEach(
+ s -> {
+ try {
+ s.getAsyncRemote()
+ .sendObject(
+ mapper.writeValueAsString(map),
+ result -> {
+ if (result.getException() != null) {
+ logger.warn(
+ "Unable to send message: "
+ + result.getException());
+ }
+ });
+ } catch (JsonProcessingException e) {
+ logger.error("Unable to send message", e);
+ }
+ });
}
}
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index 08e564fbe..f814fc441 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -19,7 +19,7 @@ cryostat.discovery.podman.enabled=true
cryostat.discovery.docker.enabled=true
quarkus.datasource.devservices.enabled=true
-quarkus.datasource.devservices.image-name=quay.io/cryostat/cryostat-db
+quarkus.datasource.devservices.image-name=quay.io/andrewazores/cryostat-db:bisect-2
# !!! prod databases must set this configuration parameter some other way via a secret !!!
quarkus.datasource.devservices.container-env.PG_ENCRYPT_KEY=examplekey
diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties
index 3af3648d8..f15a4474e 100644
--- a/src/main/resources/application-test.properties
+++ b/src/main/resources/application-test.properties
@@ -5,10 +5,10 @@ cryostat.discovery.podman.enabled=true
cryostat.discovery.docker.enabled=true
cryostat.auth.disabled=true
-quarkus.test.env.JAVA_OPTS_APPEND=-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9091 -Dcom.sun.management.jmxremote.rmi.port=9091 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.local.only=false
+quarkus.test.env.JAVA_OPTS_APPEND=-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dcom.sun.management.jmxremote.autodiscovery=true -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9091 -Dcom.sun.management.jmxremote.rmi.port=9091 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.local.only=false
quarkus.datasource.devservices.enabled=true
-quarkus.datasource.devservices.image-name=quay.io/cryostat/cryostat-db
+quarkus.datasource.devservices.image-name=quay.io/andrewazores/cryostat-db:bisect-2
# !!! prod databases must set this configuration parameter some other way via a secret !!!
quarkus.datasource.devservices.container-env.PG_ENCRYPT_KEY=examplekey
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 839db9919..6a7fc93ee 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -4,8 +4,6 @@ cryostat.discovery.podman.enabled=false
cryostat.discovery.docker.enabled=false
quarkus.test.integration-test-profile=test
-cryostat.messaging.queue.size=1024
-
quarkus.http.auth.proactive=false
quarkus.http.host=0.0.0.0
quarkus.http.port=8181
diff --git a/src/test/java/itest/CustomTargetsIT.java b/src/test/java/itest/CustomTargetsIT.java
new file mode 100644
index 000000000..ac0096959
--- /dev/null
+++ b/src/test/java/itest/CustomTargetsIT.java
@@ -0,0 +1,406 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import io.cryostat.util.HttpMimeType;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import io.vertx.core.MultiMap;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import itest.bases.StandardSelfTest;
+import itest.util.ITestCleanupFailedException;
+import itest.util.http.JvmIdWebRequest;
+import itest.util.http.StoredCredential;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+@QuarkusIntegrationTest
+@Disabled("TODO")
+@TestMethodOrder(OrderAnnotation.class)
+public class CustomTargetsIT extends StandardSelfTest {
+
+ private final ExecutorService worker = Executors.newCachedThreadPool();
+ static final Map NULL_RESULT = new HashMap<>();
+ private String itestJvmId;
+ private static StoredCredential storedCredential;
+
+ static {
+ NULL_RESULT.put("result", null);
+ }
+
+ @BeforeEach
+ void setup() throws InterruptedException, ExecutionException, TimeoutException {
+ itestJvmId =
+ JvmIdWebRequest.jvmIdRequest(
+ "service:jmx:rmi:///jndi/rmi://cryostat-itests:9091/jmxrmi");
+ }
+
+ @AfterAll
+ static void cleanup() throws Exception {
+ // Delete credentials to clean up
+ CompletableFuture deleteResponse = new CompletableFuture<>();
+ webClient
+ .delete("/api/v2.2/credentials/" + storedCredential.id)
+ .send(
+ ar -> {
+ if (assertRequestStatus(ar, deleteResponse)) {
+ deleteResponse.complete(ar.result().bodyAsJsonObject());
+ }
+ });
+
+ JsonObject expectedDeleteResponse =
+ new JsonObject(
+ Map.of(
+ "meta",
+ Map.of("type", HttpMimeType.JSON.mime(), "status", "OK"),
+ "data",
+ NULL_RESULT));
+ try {
+ MatcherAssert.assertThat(
+ deleteResponse.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS),
+ Matchers.equalTo(expectedDeleteResponse));
+ } catch (Exception e) {
+ logger.error(
+ new ITestCleanupFailedException(
+ String.format(
+ "Failed to clean up credential with ID %d",
+ storedCredential.id),
+ e));
+ }
+ }
+
+ @Test
+ @Order(1)
+ void shouldBeAbleToTestTargetConnection() throws InterruptedException, ExecutionException {
+ MultiMap form = MultiMap.caseInsensitiveMultiMap();
+ form.add("connectUrl", "localhost:0");
+ form.add("alias", "self");
+
+ CompletableFuture response = new CompletableFuture<>();
+ webClient
+ .post("/api/v2/targets?dryrun=true")
+ .sendForm(
+ form,
+ ar -> {
+ assertRequestStatus(ar, response);
+ // Assert 202 since localhost:0 jvm already exists
+ MatcherAssert.assertThat(
+ ar.result().statusCode(), Matchers.equalTo(202));
+ response.complete(ar.result().bodyAsJsonObject());
+ });
+ JsonObject body = response.get().getJsonObject("data").getJsonObject("result");
+ MatcherAssert.assertThat(body.getString("connectUrl"), Matchers.equalTo("localhost:0"));
+ MatcherAssert.assertThat(body.getString("alias"), Matchers.equalTo("self"));
+ }
+
+ @Test
+ @Order(2)
+ void targetShouldNotAppearInListing() throws InterruptedException, ExecutionException {
+ CompletableFuture response = new CompletableFuture<>();
+ webClient
+ .get("/api/v1/targets")
+ .send(
+ ar -> {
+ assertRequestStatus(ar, response);
+ response.complete(ar.result().bodyAsJsonArray());
+ });
+ JsonArray body = response.get();
+ MatcherAssert.assertThat(body, Matchers.notNullValue());
+ MatcherAssert.assertThat(body.size(), Matchers.equalTo(1));
+
+ JsonObject selfJdp =
+ new JsonObject(
+ Map.of(
+ "jvmId",
+ itestJvmId,
+ "alias",
+ "io.cryostat.Cryostat",
+ "connectUrl",
+ "service:jmx:rmi:///jndi/rmi://cryostat-itests:9091/jmxrmi",
+ "labels",
+ Map.of(),
+ "annotations",
+ Map.of(
+ "cryostat",
+ Map.of(
+ "REALM",
+ "JDP",
+ "HOST",
+ "cryostat-itests",
+ "PORT",
+ "9091",
+ "JAVA_MAIN",
+ "io.cryostat.Cryostat"),
+ "platform",
+ Map.of())));
+ MatcherAssert.assertThat(body, Matchers.contains(selfJdp));
+ }
+
+ @Test
+ @Order(3)
+ void shouldBeAbleToDefineTarget()
+ throws TimeoutException, ExecutionException, InterruptedException {
+ MultiMap form = MultiMap.caseInsensitiveMultiMap();
+ form.add("connectUrl", "localhost:0");
+ form.add("alias", "self");
+ form.add("username", "username");
+ form.add("password", "password");
+
+ CountDownLatch latch = new CountDownLatch(3);
+
+ Future resultFuture1 =
+ worker.submit(
+ () -> {
+ try {
+ return expectNotification("CredentialsStored", 15, TimeUnit.SECONDS)
+ .get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ latch.countDown();
+ }
+ });
+
+ Future resultFuture2 =
+ worker.submit(
+ () -> {
+ try {
+ return expectNotification(
+ "TargetJvmDiscovery", 15, TimeUnit.SECONDS)
+ .get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ latch.countDown();
+ }
+ });
+
+ Thread.sleep(5000); // Sleep to setup notification listening before query resolves
+
+ CompletableFuture response = new CompletableFuture<>();
+ webClient
+ .post("/api/v2/targets?storeCredentials=true")
+ .sendForm(
+ form,
+ ar -> {
+ assertRequestStatus(ar, response);
+ response.complete(ar.result().bodyAsJsonObject());
+ latch.countDown();
+ });
+ latch.await(30, TimeUnit.SECONDS);
+
+ JsonObject body = response.get().getJsonObject("data").getJsonObject("result");
+ MatcherAssert.assertThat(body.getString("connectUrl"), Matchers.equalTo("localhost:0"));
+ MatcherAssert.assertThat(body.getString("alias"), Matchers.equalTo("self"));
+
+ JsonObject result1 = resultFuture1.get();
+
+ JsonObject message = result1.getJsonObject("message");
+
+ storedCredential =
+ new StoredCredential(
+ message.getInteger("id"),
+ message.getString("matchExpression"),
+ message.getInteger("numMatchingTargets"));
+
+ MatcherAssert.assertThat(storedCredential.id, Matchers.any(Integer.class));
+ MatcherAssert.assertThat(
+ storedCredential.matchExpression,
+ Matchers.equalTo("target.connectUrl == \"localhost:0\""));
+ MatcherAssert.assertThat(
+ storedCredential.numMatchingTargets, Matchers.equalTo(Integer.valueOf(1)));
+
+ JsonObject result2 = resultFuture2.get();
+ JsonObject event = result2.getJsonObject("message").getJsonObject("event");
+ MatcherAssert.assertThat(event.getString("kind"), Matchers.equalTo("FOUND"));
+ MatcherAssert.assertThat(
+ event.getJsonObject("serviceRef").getString("connectUrl"),
+ Matchers.equalTo("localhost:0"));
+ MatcherAssert.assertThat(
+ event.getJsonObject("serviceRef").getString("alias"), Matchers.equalTo("self"));
+ }
+
+ @Test
+ @Order(4)
+ void targetShouldAppearInListing()
+ throws ExecutionException, InterruptedException, TimeoutException {
+ CompletableFuture response = new CompletableFuture<>();
+ webClient
+ .get("/api/v1/targets")
+ .send(
+ ar -> {
+ assertRequestStatus(ar, response);
+ response.complete(ar.result().bodyAsJsonArray());
+ });
+ JsonArray body = response.get();
+ MatcherAssert.assertThat(body, Matchers.notNullValue());
+ MatcherAssert.assertThat(body.size(), Matchers.equalTo(2));
+
+ JsonObject selfJdp =
+ new JsonObject(
+ Map.of(
+ "jvmId",
+ itestJvmId,
+ "alias",
+ "io.cryostat.Cryostat",
+ "connectUrl",
+ "service:jmx:rmi:///jndi/rmi://cryostat-itests:9091/jmxrmi",
+ "labels",
+ Map.of(),
+ "annotations",
+ Map.of(
+ "cryostat",
+ Map.of(
+ "REALM",
+ "JDP",
+ "HOST",
+ "cryostat-itests",
+ "PORT",
+ "9091",
+ "JAVA_MAIN",
+ "io.cryostat.Cryostat"),
+ "platform",
+ Map.of())));
+ JsonObject selfCustom =
+ new JsonObject(
+ Map.of(
+ "jvmId",
+ itestJvmId,
+ "alias",
+ "self",
+ "connectUrl",
+ "localhost:0",
+ "labels",
+ Map.of(),
+ "annotations",
+ Map.of(
+ "cryostat",
+ Map.of("REALM", "Custom Targets"),
+ "platform",
+ Map.of())));
+ MatcherAssert.assertThat(body, Matchers.containsInAnyOrder(selfJdp, selfCustom));
+ }
+
+ @Test
+ @Order(5)
+ void shouldBeAbleToDeleteTarget()
+ throws TimeoutException, ExecutionException, InterruptedException {
+ CountDownLatch latch = new CountDownLatch(2);
+
+ worker.submit(
+ () -> {
+ try {
+ expectNotification("TargetJvmDiscovery", 5, TimeUnit.SECONDS)
+ .thenAcceptAsync(
+ notification -> {
+ JsonObject event =
+ notification
+ .getJsonObject("message")
+ .getJsonObject("event");
+ MatcherAssert.assertThat(
+ event.getString("kind"),
+ Matchers.equalTo("LOST"));
+ MatcherAssert.assertThat(
+ event.getJsonObject("serviceRef")
+ .getString("connectUrl"),
+ Matchers.equalTo("localhost:0"));
+ MatcherAssert.assertThat(
+ event.getJsonObject("serviceRef")
+ .getString("alias"),
+ Matchers.equalTo("self"));
+ latch.countDown();
+ })
+ .get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ CompletableFuture response = new CompletableFuture<>();
+ webClient
+ .delete("/api/v2/targets/localhost:0")
+ .send(
+ ar -> {
+ assertRequestStatus(ar, response);
+ response.complete(null);
+ latch.countDown();
+ });
+
+ latch.await(5, TimeUnit.SECONDS);
+ }
+
+ @Test
+ @Order(6)
+ void targetShouldNoLongerAppearInListing() throws ExecutionException, InterruptedException {
+ CompletableFuture response = new CompletableFuture<>();
+ webClient
+ .get("/api/v1/targets")
+ .send(
+ ar -> {
+ assertRequestStatus(ar, response);
+ response.complete(ar.result().bodyAsJsonArray());
+ });
+ JsonArray body = response.get();
+ MatcherAssert.assertThat(body, Matchers.notNullValue());
+ MatcherAssert.assertThat(body.size(), Matchers.equalTo(1));
+
+ JsonObject selfJdp =
+ new JsonObject(
+ Map.of(
+ "jvmId",
+ itestJvmId,
+ "alias",
+ "io.cryostat.Cryostat",
+ "connectUrl",
+ "service:jmx:rmi:///jndi/rmi://cryostat-itests:9091/jmxrmi",
+ "labels",
+ Map.of(),
+ "annotations",
+ Map.of(
+ "cryostat",
+ Map.of(
+ "REALM",
+ "JDP",
+ "HOST",
+ "cryostat-itests",
+ "PORT",
+ "9091",
+ "JAVA_MAIN",
+ "io.cryostat.Cryostat"),
+ "platform",
+ Map.of())));
+ MatcherAssert.assertThat(body, Matchers.contains(selfJdp));
+ }
+}
diff --git a/src/test/java/itest/CustomTargetsTest.java b/src/test/java/itest/CustomTargetsTest.java
deleted file mode 100644
index b7a08d123..000000000
--- a/src/test/java/itest/CustomTargetsTest.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * 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.net.UnknownHostException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import io.cryostat.util.HttpMimeType;
-
-import io.quarkus.test.junit.QuarkusTest;
-import io.vertx.core.MultiMap;
-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.StandardSelfTest;
-import itest.util.ITestCleanupFailedException;
-import itest.util.http.JvmIdWebRequest;
-import itest.util.http.StoredCredential;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.hamcrest.MatcherAssert;
-import org.hamcrest.Matchers;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
-import org.junit.jupiter.api.Order;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-
-@QuarkusTest
-@TestMethodOrder(OrderAnnotation.class)
-public class CustomTargetsTest extends StandardSelfTest {
-
- private final ExecutorService worker = Executors.newCachedThreadPool();
- private static String itestJvmId;
- private static StoredCredential storedCredential;
-
- static String JMX_URL_ENCODED = URLEncodedUtils.formatSegments(SELF_JMX_URL).substring(1);
-
- @BeforeAll
- static void removeTestHarnessTargetDefinition()
- throws InterruptedException,
- ExecutionException,
- TimeoutException,
- UnknownHostException {
- itestJvmId = JvmIdWebRequest.jvmIdRequest(SELF_JMX_URL);
-
- deleteSelfCustomTarget();
-
- JsonArray list =
- webClient
- .extensions()
- .get("/api/v3/targets", true, REQUEST_TIMEOUT_SECONDS)
- .bodyAsJsonArray();
- if (!list.isEmpty()) throw new IllegalStateException();
- }
-
- @AfterAll
- static void restoreTestHarnessTargetDefinition()
- throws InterruptedException,
- ExecutionException,
- TimeoutException,
- UnknownHostException {
- waitForDiscovery(0);
- }
-
- @AfterAll
- static void cleanup() throws Exception {
- // Delete credentials to clean up
- if (storedCredential == null) {
- return;
- }
- CompletableFuture deleteResponse = new CompletableFuture<>();
- webClient
- .delete("/api/v2.2/credentials/" + storedCredential.id)
- .send(
- ar -> {
- if (assertRequestStatus(ar, deleteResponse)) {
- deleteResponse.complete(ar.result().bodyAsJsonObject());
- }
- });
-
- Map nullResult = new HashMap<>();
- nullResult.put("result", null);
- JsonObject expectedDeleteResponse =
- new JsonObject(
- Map.of(
- "meta",
- Map.of("type", HttpMimeType.JSON.mime(), "status", "OK"),
- "data",
- nullResult));
- try {
- MatcherAssert.assertThat(
- deleteResponse.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS),
- Matchers.equalTo(expectedDeleteResponse));
- } catch (Exception e) {
- logger.error(
- new ITestCleanupFailedException(
- String.format(
- "Failed to clean up credential with ID %d",
- storedCredential.id),
- e));
- }
- }
-
- @Test
- @Order(1)
- void shouldBeAbleToTestTargetConnection()
- throws InterruptedException, ExecutionException, TimeoutException {
- HttpResponse response =
- webClient
- .extensions()
- .post(
- "/api/v2/targets?dryrun=true",
- true,
- Buffer.buffer(
- JsonObject.of("connectUrl", SELF_JMX_URL, "alias", "self")
- .encode()),
- REQUEST_TIMEOUT_SECONDS);
- MatcherAssert.assertThat(response.statusCode(), Matchers.equalTo(202));
- JsonObject body = response.bodyAsJsonObject().getJsonObject("data").getJsonObject("result");
- MatcherAssert.assertThat(body.getString("connectUrl"), Matchers.equalTo(SELF_JMX_URL));
- MatcherAssert.assertThat(body.getString("alias"), Matchers.equalTo("self"));
- MatcherAssert.assertThat(body.getString("jvmId"), Matchers.equalTo(itestJvmId));
-
- JsonArray list =
- webClient
- .extensions()
- .get("/api/v3/targets", true, REQUEST_TIMEOUT_SECONDS)
- .bodyAsJsonArray();
- MatcherAssert.assertThat(list, Matchers.notNullValue());
- MatcherAssert.assertThat(list.size(), Matchers.equalTo(0));
- }
-
- @Test
- @Order(2)
- void shouldBeAbleToDefineTarget()
- throws TimeoutException, ExecutionException, InterruptedException {
- MultiMap form = MultiMap.caseInsensitiveMultiMap();
- String alias = UUID.randomUUID().toString();
- form.add("connectUrl", SELF_JMX_URL);
- form.add("alias", alias);
- form.add("username", "username");
- form.add("password", "password");
-
- CountDownLatch latch = new CountDownLatch(2);
-
- Future resultFuture1 =
- worker.submit(
- () -> {
- try {
- return expectNotification(
- "CredentialsStored",
- REQUEST_TIMEOUT_SECONDS,
- TimeUnit.SECONDS)
- .get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- } finally {
- latch.countDown();
- }
- });
-
- Future resultFuture2 =
- worker.submit(
- () -> {
- try {
- return expectNotification(
- "TargetJvmDiscovery",
- REQUEST_TIMEOUT_SECONDS,
- TimeUnit.SECONDS)
- .get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- } finally {
- latch.countDown();
- }
- });
-
- Thread.sleep(500);
-
- HttpResponse response =
- webClient
- .extensions()
- .post(
- "/api/v2/targets?storeCredentials=true",
- true,
- form,
- REQUEST_TIMEOUT_SECONDS);
- MatcherAssert.assertThat(response.statusCode(), Matchers.equalTo(201));
-
- JsonObject body = response.bodyAsJsonObject().getJsonObject("data").getJsonObject("result");
-
- latch.await(30, TimeUnit.SECONDS);
-
- MatcherAssert.assertThat(body.getString("connectUrl"), Matchers.equalTo(SELF_JMX_URL));
- MatcherAssert.assertThat(body.getString("alias"), Matchers.equalTo(alias));
-
- JsonObject result1 = resultFuture1.get();
-
- JsonObject message = result1.getJsonObject("message");
-
- storedCredential =
- new StoredCredential(
- message.getInteger("id"),
- message.getString("matchExpression"),
- message.getInteger("numMatchingTargets"));
-
- MatcherAssert.assertThat(storedCredential.id, Matchers.any(Integer.class));
- MatcherAssert.assertThat(
- storedCredential.matchExpression,
- Matchers.equalTo(String.format("target.connectUrl == \"%s\"", SELF_JMX_URL)));
- // FIXME this is currently always emitted as 0. Do we really need this to be included at
- // all?
- // MatcherAssert.assertThat(
- // storedCredential.numMatchingTargets, Matchers.equalTo(Integer.valueOf(1)));
-
- JsonObject result2 = resultFuture2.get();
- JsonObject foundDiscoveryEvent = result2.getJsonObject("message").getJsonObject("event");
- MatcherAssert.assertThat(foundDiscoveryEvent.getString("kind"), Matchers.equalTo("FOUND"));
- MatcherAssert.assertThat(
- foundDiscoveryEvent.getJsonObject("serviceRef").getString("connectUrl"),
- Matchers.equalTo(SELF_JMX_URL));
- MatcherAssert.assertThat(
- foundDiscoveryEvent.getJsonObject("serviceRef").getString("alias"),
- Matchers.equalTo(alias));
-
- HttpResponse listResponse =
- webClient.extensions().get("/api/v1/targets", true, REQUEST_TIMEOUT_SECONDS);
- MatcherAssert.assertThat(listResponse.statusCode(), Matchers.equalTo(200));
- JsonArray list = listResponse.bodyAsJsonArray();
- MatcherAssert.assertThat(list, Matchers.notNullValue());
- MatcherAssert.assertThat(list.size(), Matchers.equalTo(1));
- JsonObject item = list.getJsonObject(0);
- MatcherAssert.assertThat(item.getString("jvmId"), Matchers.equalTo(itestJvmId));
- MatcherAssert.assertThat(item.getString("alias"), Matchers.equalTo(alias));
- MatcherAssert.assertThat(item.getString("connectUrl"), Matchers.equalTo(SELF_JMX_URL));
- MatcherAssert.assertThat(item.getJsonObject("labels"), Matchers.equalTo(new JsonObject()));
- MatcherAssert.assertThat(
- item.getJsonObject("annotations"),
- Matchers.equalTo(
- new JsonObject(
- Map.of(
- "platform",
- Map.of(),
- "cryostat",
- Map.of("REALM", "Custom Targets")))));
- }
-
- @Test
- @Order(3)
- void shouldBeAbleToDeleteTarget()
- throws TimeoutException, ExecutionException, InterruptedException {
- CountDownLatch latch = new CountDownLatch(1);
-
- worker.submit(
- () -> {
- try {
- expectNotification(
- "TargetJvmDiscovery",
- REQUEST_TIMEOUT_SECONDS,
- TimeUnit.SECONDS)
- .thenAcceptAsync(
- notification -> {
- JsonObject event =
- notification
- .getJsonObject("message")
- .getJsonObject("event");
- MatcherAssert.assertThat(
- event.getString("kind"),
- Matchers.equalTo("LOST"));
- MatcherAssert.assertThat(
- event.getJsonObject("serviceRef")
- .getString("connectUrl"),
- Matchers.equalTo("localhost:0"));
- MatcherAssert.assertThat(
- event.getJsonObject("serviceRef")
- .getString("alias"),
- Matchers.equalTo("self"));
- latch.countDown();
- })
- .get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
-
- webClient
- .extensions()
- .delete(
- String.format("/api/v2/targets/%s", JMX_URL_ENCODED),
- true,
- REQUEST_TIMEOUT_SECONDS);
-
- latch.await(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-
- HttpResponse listResponse =
- webClient.extensions().get("/api/v1/targets", true, REQUEST_TIMEOUT_SECONDS);
- MatcherAssert.assertThat(listResponse.statusCode(), Matchers.equalTo(200));
- JsonArray list = listResponse.bodyAsJsonArray();
- MatcherAssert.assertThat(list, Matchers.notNullValue());
- MatcherAssert.assertThat(list.size(), Matchers.equalTo(0));
- }
-}
diff --git a/src/test/java/itest/SnapshotTest.java b/src/test/java/itest/SnapshotTest.java
index ece42cdcc..1f9d46125 100644
--- a/src/test/java/itest/SnapshotTest.java
+++ b/src/test/java/itest/SnapshotTest.java
@@ -15,7 +15,6 @@
*/
package itest;
-import java.net.URI;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@@ -150,13 +149,7 @@ void testPostV1ShouldCreateSnapshot() throws Exception {
form.add("recordingName", TEST_RECORDING_NAME);
form.add("duration", "5");
form.add("events", "template=ALL");
- webClient
- .extensions()
- .post(
- String.format("%s/recordings", v1RequestUrl()),
- true,
- form,
- REQUEST_TIMEOUT_SECONDS);
+ webClient.extensions().post(String.format("%s/recordings", v1RequestUrl()), true, form, 5);
// Create a snapshot recording of all events at that time
webClient
@@ -184,7 +177,7 @@ void testPostV1ShouldCreateSnapshot() throws Exception {
.delete(
String.format("%s/recordings/%s", v1RequestUrl(), TEST_RECORDING_NAME),
true,
- REQUEST_TIMEOUT_SECONDS);
+ 5);
webClient
.extensions()
.delete(
@@ -193,7 +186,7 @@ void testPostV1ShouldCreateSnapshot() throws Exception {
v1RequestUrl(),
snapshotName.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)),
true,
- REQUEST_TIMEOUT_SECONDS);
+ 5);
}
@Test
@@ -224,13 +217,7 @@ void testPostV2ShouldCreateSnapshot() throws Exception {
form.add("recordingName", TEST_RECORDING_NAME);
form.add("duration", "5");
form.add("events", "template=ALL");
- webClient
- .extensions()
- .post(
- String.format("%s/recordings", v1RequestUrl()),
- true,
- form,
- REQUEST_TIMEOUT_SECONDS);
+ webClient.extensions().post(String.format("%s/recordings", v1RequestUrl()), true, form, 5);
// Create a snapshot recording of all events at that time
CompletableFuture createResponse = new CompletableFuture<>();
@@ -278,13 +265,7 @@ void testPostV2ShouldCreateSnapshot() throws Exception {
Matchers.equalTo("/api/v3/activedownload/" + result.getLong("id")));
MatcherAssert.assertThat(
result.getString("reportUrl"),
- Matchers.equalTo(
- URI.create(
- String.format(
- "%s/reports/%d",
- selfCustomTargetLocation,
- result.getLong("remoteId")))
- .getPath()));
+ Matchers.equalTo("/api/v3/targets/1/reports/" + result.getLong("remoteId")));
MatcherAssert.assertThat(result.getLong("expiry"), Matchers.nullValue());
webClient
@@ -292,7 +273,7 @@ void testPostV2ShouldCreateSnapshot() throws Exception {
.delete(
String.format("%s/recordings/%s", v1RequestUrl(), TEST_RECORDING_NAME),
true,
- REQUEST_TIMEOUT_SECONDS);
+ 5);
webClient
.extensions()
.delete(
@@ -301,7 +282,7 @@ void testPostV2ShouldCreateSnapshot() throws Exception {
v1RequestUrl(),
snapshotName.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)),
true,
- REQUEST_TIMEOUT_SECONDS);
+ 5);
}
@Test
diff --git a/src/test/java/itest/UploadRecordingTest.java b/src/test/java/itest/UploadRecordingTest.java
index 0ef11155b..437b87e9a 100644
--- a/src/test/java/itest/UploadRecordingTest.java
+++ b/src/test/java/itest/UploadRecordingTest.java
@@ -108,7 +108,7 @@ public void shouldLoadRecordingToDatasource() throws Exception {
"/api/v1/targets/%s/recordings/%s/upload",
getSelfReferenceConnectUrlEncoded(), RECORDING_NAME),
true,
- (Buffer) null,
+ null,
0);
MatcherAssert.assertThat(resp.statusCode(), Matchers.equalTo(200));
diff --git a/src/test/java/itest/bases/StandardSelfTest.java b/src/test/java/itest/bases/StandardSelfTest.java
index 693f085f1..ed6e6026d 100644
--- a/src/test/java/itest/bases/StandardSelfTest.java
+++ b/src/test/java/itest/bases/StandardSelfTest.java
@@ -55,8 +55,8 @@
public abstract class StandardSelfTest {
- public static final String SELF_JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:0/jmxrmi";
- public static final String SELFTEST_ALIAS = "selftest";
+ private static final String SELF_JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:0/jmxrmi";
+ private 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 = new ObjectMapper();
@@ -81,36 +81,62 @@ public static void assertPostconditions() throws Exception {
}
public static void assertNoRecordings() throws Exception {
- JsonArray listResp =
- webClient
- .extensions()
- .get(
- String.format(
- "/api/v1/targets/%s/recordings",
- getSelfReferenceConnectUrlEncoded()),
- true,
- REQUEST_TIMEOUT_SECONDS)
- .bodyAsJsonArray();
+ CompletableFuture listFuture = new CompletableFuture<>();
+ webClient
+ .get(
+ String.format(
+ "/api/v1/targets/%s/recordings",
+ getSelfReferenceConnectUrlEncoded()))
+ .basicAuthentication("user", "pass")
+ .send(
+ ar -> {
+ if (assertRequestStatus(ar, listFuture)) {
+ listFuture.complete(ar.result().bodyAsJsonArray());
+ }
+ });
+ JsonArray listResp = listFuture.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (!listResp.isEmpty()) {
throw new ITestCleanupFailedException(
String.format("Unexpected recordings:\n%s", listResp.encodePrettily()));
}
}
- @AfterAll
- public static void deleteSelfCustomTarget()
- throws InterruptedException, ExecutionException, TimeoutException {
- if (!selfCustomTargetExists()) {
+ // @AfterAll
+ public static void deleteSelfCustomTarget() {
+ if (StringUtils.isBlank(selfCustomTargetLocation)) {
return;
}
logger.infov("Deleting self custom target at {0}", selfCustomTargetLocation);
String path = URI.create(selfCustomTargetLocation).getPath();
- HttpResponse resp =
- webClient.extensions().delete(path, true, REQUEST_TIMEOUT_SECONDS);
- logger.infov(
- "DELETE {0} -> HTTP {1} {2}: [{3}]",
- path, resp.statusCode(), resp.statusMessage(), resp.headers());
- selfCustomTargetLocation = null;
+ CompletableFuture future = new CompletableFuture<>();
+ try {
+ WORKER.submit(
+ () -> {
+ webClient
+ .delete(path)
+ .basicAuthentication("user", "pass")
+ .timeout(5000)
+ .send(
+ ar -> {
+ if (ar.failed()) {
+ logger.error(ar.cause());
+ future.completeExceptionally(ar.cause());
+ return;
+ }
+ HttpResponse resp = ar.result();
+ logger.infov(
+ "DELETE {0} -> HTTP {1} {2}: [{3}]",
+ path,
+ resp.statusCode(),
+ resp.statusMessage(),
+ resp.headers());
+ future.complete(null);
+ });
+ });
+ selfCustomTargetLocation = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ logger.error(e);
+ }
}
public static void waitForDiscovery(int otherTargetsCount) {
@@ -126,7 +152,7 @@ public static void waitForDiscovery(int otherTargetsCount) {
.get("/api/v3/targets")
.basicAuthentication("user", "pass")
.as(BodyCodec.jsonArray())
- .timeout(TimeUnit.SECONDS.toMillis(REQUEST_TIMEOUT_SECONDS))
+ .timeout(5000)
.send(
ar -> {
if (ar.failed()) {
@@ -168,21 +194,38 @@ private static boolean selfCustomTargetExists() {
if (StringUtils.isBlank(selfCustomTargetLocation)) {
return false;
}
+ CompletableFuture future = new CompletableFuture<>();
try {
- HttpResponse resp =
- webClient
- .extensions()
- .get(selfCustomTargetLocation, true, REQUEST_TIMEOUT_SECONDS);
- logger.infov(
- "POST /api/v2/targets -> HTTP {0} {1}: [{2}]",
- resp.statusCode(), resp.statusMessage(), resp.headers());
- boolean result = HttpStatusCodeIdentifier.isSuccessCode(resp.statusCode());
+ WORKER.submit(
+ () -> {
+ webClient
+ .getAbs(selfCustomTargetLocation)
+ .basicAuthentication("user", "pass")
+ .timeout(5000)
+ .send(
+ ar -> {
+ if (ar.failed()) {
+ logger.error(ar.cause());
+ future.completeExceptionally(ar.cause());
+ return;
+ }
+ HttpResponse resp = ar.result();
+ logger.infov(
+ "POST /api/v2/targets -> HTTP {0} {1}: [{2}]",
+ resp.statusCode(),
+ resp.statusMessage(),
+ resp.headers());
+ future.complete(
+ HttpStatusCodeIdentifier.isSuccessCode(
+ resp.statusCode()));
+ });
+ });
+ boolean result = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (!result) {
selfCustomTargetLocation = null;
}
return result;
} catch (Exception e) {
- selfCustomTargetLocation = null;
logger.error(e);
return false;
}
@@ -193,45 +236,88 @@ private static void tryDefineSelfCustomTarget() {
return;
}
logger.info("Trying to define self-referential custom target...");
- JsonObject self =
- new JsonObject(Map.of("connectUrl", SELF_JMX_URL, "alias", SELFTEST_ALIAS));
- HttpResponse resp;
+ CompletableFuture future = new CompletableFuture<>();
try {
- resp =
- webClient
- .extensions()
- .post(
- "/api/v2/targets",
- true,
- Buffer.buffer(self.encode()),
- REQUEST_TIMEOUT_SECONDS);
- logger.infov(
- "POST /api/v2/targets -> HTTP {0} {1}: [{2}]",
- resp.statusCode(), resp.statusMessage(), resp.headers());
- if (!HttpStatusCodeIdentifier.isSuccessCode(resp.statusCode())) {
- throw new IllegalStateException(Integer.toString(resp.statusCode()));
- }
- selfCustomTargetLocation =
- URI.create(resp.headers().get(HttpHeaders.LOCATION)).getPath();
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new IllegalStateException(e);
+ JsonObject self =
+ new JsonObject(Map.of("connectUrl", SELF_JMX_URL, "alias", SELFTEST_ALIAS));
+ WORKER.submit(
+ () -> {
+ webClient
+ .post("/api/v2/targets")
+ .basicAuthentication("user", "pass")
+ .timeout(5000)
+ .sendJson(
+ self,
+ ar -> {
+ if (ar.failed()) {
+ logger.error(ar.cause());
+ future.completeExceptionally(ar.cause());
+ return;
+ }
+ HttpResponse resp = ar.result();
+ logger.infov(
+ "POST /api/v2/targets -> HTTP {0} {1}: [{2}]",
+ resp.statusCode(),
+ resp.statusMessage(),
+ resp.headers());
+ if (HttpStatusCodeIdentifier.isSuccessCode(
+ resp.statusCode())) {
+ future.complete(
+ resp.headers().get(HttpHeaders.LOCATION));
+ } else {
+ future.completeExceptionally(
+ new IllegalStateException(
+ Integer.toString(
+ resp.statusCode())));
+ }
+ });
+ });
+ selfCustomTargetLocation = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ logger.error(e);
}
}
public static String getSelfReferenceConnectUrl() {
+ tryDefineSelfCustomTarget();
+ CompletableFuture future = new CompletableFuture<>();
+ WORKER.submit(
+ () -> {
+ String path = URI.create(selfCustomTargetLocation).getPath();
+ webClient
+ .get(path)
+ .basicAuthentication("user", "pass")
+ .as(BodyCodec.jsonObject())
+ .timeout(5000)
+ .send(
+ ar -> {
+ if (ar.failed()) {
+ logger.error(ar.cause());
+ future.completeExceptionally(ar.cause());
+ return;
+ }
+ HttpResponse resp = ar.result();
+ JsonObject body = resp.body();
+ logger.infov(
+ "GET {0} -> HTTP {1} {2}: [{3}] = {4}",
+ path,
+ resp.statusCode(),
+ resp.statusMessage(),
+ resp.headers(),
+ body);
+ if (!HttpStatusCodeIdentifier.isSuccessCode(
+ resp.statusCode())) {
+ future.completeExceptionally(
+ new IllegalStateException(
+ Integer.toString(resp.statusCode())));
+ return;
+ }
+ future.complete(body);
+ });
+ });
try {
- tryDefineSelfCustomTarget();
- String path = URI.create(selfCustomTargetLocation).getPath();
- HttpResponse resp =
- webClient.extensions().get(path, true, REQUEST_TIMEOUT_SECONDS);
- JsonObject body = resp.bodyAsJsonObject();
- logger.infov(
- "GET {0} -> HTTP {1} {2}: [{3}] = {4}",
- path, resp.statusCode(), resp.statusMessage(), resp.headers(), body);
- if (!HttpStatusCodeIdentifier.isSuccessCode(resp.statusCode())) {
- throw new IllegalStateException(Integer.toString(resp.statusCode()));
- }
- return body.getString("connectUrl");
+ JsonObject obj = future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ return obj.getString("connectUrl");
} catch (Exception e) {
throw new RuntimeException("Could not determine own connectUrl", e);
}
@@ -247,12 +333,8 @@ public static String getSelfReferenceConnectUrlEncoded() {
public static CompletableFuture expectNotification(
String category, long timeout, TimeUnit unit)
throws TimeoutException, ExecutionException, InterruptedException {
- logger.infov(
- "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(
getNotificationsUrl().get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS),
ar -> {
@@ -260,8 +342,7 @@ public static CompletableFuture expectNotification(
future.completeExceptionally(ar.cause());
return;
}
- a[0] = ar.result();
- var ws = a[0];
+ WebSocket ws = ar.result();
ws.handler(
m -> {
@@ -269,8 +350,6 @@ public static CompletableFuture expectNotification(
JsonObject meta = resp.getJsonObject("meta");
String c = meta.getString("category");
if (Objects.equals(c, category)) {
- logger.infov(
- "Received expected \"{0}\" message", category);
ws.end(unused -> future.complete(resp));
ws.close();
}
@@ -286,7 +365,7 @@ public static CompletableFuture expectNotification(
.writeTextMessage("");
});
- return future.orTimeout(timeout, unit).whenComplete((o, t) -> a[0].close());
+ return future.orTimeout(timeout, unit);
}
public static boolean assertRequestStatus(
diff --git a/src/test/java/itest/util/Utils.java b/src/test/java/itest/util/Utils.java
index c74256f22..61ef57bd4 100644
--- a/src/test/java/itest/util/Utils.java
+++ b/src/test/java/itest/util/Utils.java
@@ -83,13 +83,7 @@ public static FileSystem getFileSystem() {
}
public interface RedirectExtensions {
- HttpResponse get(String url, boolean authentication, int timeout)
- throws InterruptedException, ExecutionException, TimeoutException;
-
- HttpResponse post(String url, boolean authentication, Buffer payload, int timeout)
- throws InterruptedException, ExecutionException, TimeoutException;
-
- HttpResponse post(String url, boolean authentication, MultiMap payload, int timeout)
+ HttpResponse post(String url, boolean authentication, MultiMap form, int timeout)
throws InterruptedException, ExecutionException, TimeoutException;
HttpResponse delete(String url, boolean authentication, int timeout)
@@ -118,66 +112,8 @@ public RedirectExtensions extensions() {
}
private class RedirectExtensionsImpl implements RedirectExtensions {
- public HttpResponse get(String url, boolean authentication, int timeout)
- throws InterruptedException, ExecutionException, TimeoutException {
- CompletableFuture> future = new CompletableFuture<>();
- RequestOptions options = new RequestOptions().setURI(url);
- HttpRequest req = TestWebClient.this.request(HttpMethod.GET, options);
- if (authentication) {
- req.basicAuthentication("user", "pass");
- }
- req.send(
- ar -> {
- if (ar.succeeded()) {
- future.complete(ar.result());
- } else {
- future.completeExceptionally(ar.cause());
- }
- });
- if (future.get().statusCode() == 308) {
- return get(future.get().getHeader("Location"), true, timeout);
- }
- return future.get(timeout, TimeUnit.SECONDS);
- }
-
- public HttpResponse post(
- String url, boolean authentication, Buffer payload, int timeout)
- throws InterruptedException, ExecutionException, TimeoutException {
- CompletableFuture> future = new CompletableFuture<>();
- RequestOptions options = new RequestOptions().setURI(url);
- HttpRequest req = TestWebClient.this.request(HttpMethod.POST, options);
- if (authentication) {
- req.basicAuthentication("user", "pass");
- }
- if (payload != null) {
- req.sendBuffer(
- payload,
- ar -> {
- if (ar.succeeded()) {
- future.complete(ar.result());
- } else {
- future.completeExceptionally(ar.cause());
- }
- });
- } else {
- req.send(
- ar -> {
- if (ar.succeeded()) {
- future.complete(ar.result());
- } else {
- future.completeExceptionally(ar.cause());
- }
- });
- }
- if (future.get().statusCode() == 308) {
- return post(
- future.get().getHeader("Location"), authentication, payload, timeout);
- }
- return future.get(timeout, TimeUnit.SECONDS);
- }
-
public HttpResponse post(
- String url, boolean authentication, MultiMap payload, int timeout)
+ String url, boolean authentication, MultiMap form, int timeout)
throws InterruptedException, ExecutionException, TimeoutException {
CompletableFuture> future = new CompletableFuture<>();
RequestOptions options = new RequestOptions().setURI(url);
@@ -185,9 +121,9 @@ public HttpResponse post(
if (authentication) {
req.basicAuthentication("user", "pass");
}
- if (payload != null) {
+ if (form != null) {
req.sendForm(
- payload,
+ form,
ar -> {
if (ar.succeeded()) {
future.complete(ar.result());
@@ -206,8 +142,7 @@ public HttpResponse post(
});
}
if (future.get().statusCode() == 308) {
- return post(
- future.get().getHeader("Location"), authentication, payload, timeout);
+ return post(future.get().getHeader("Location"), authentication, form, timeout);
}
return future.get(timeout, TimeUnit.SECONDS);
}
diff --git a/src/test/java/itest/util/http/JvmIdWebRequest.java b/src/test/java/itest/util/http/JvmIdWebRequest.java
index f9c8180b7..ad3eed3da 100644
--- a/src/test/java/itest/util/http/JvmIdWebRequest.java
+++ b/src/test/java/itest/util/http/JvmIdWebRequest.java
@@ -15,41 +15,93 @@
*/
package itest.util.http;
-import java.util.Objects;
+import java.net.URI;
+import java.util.Base64;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import com.google.gson.Gson;
+import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.client.HttpRequest;
+import io.vertx.ext.web.client.WebClient;
import itest.bases.StandardSelfTest;
import itest.util.Utils;
-import itest.util.Utils.TestWebClient;
+import org.apache.commons.lang3.tuple.Pair;
public class JvmIdWebRequest {
- public static final TestWebClient webClient = Utils.getWebClient();
+ private static final Gson gson = new Gson();
- public static String jvmIdRequest(long id)
+ public static final int REQUEST_TIMEOUT_SECONDS = 10;
+ public static final WebClient webClient = Utils.getWebClient();
+
+ // shouldn't be percent-encoded i.e.
+ // String.format("service:jmx:rmi:///jndi/rmi://%s:9091/jmxrmi", Podman.POD_NAME)
+ public static String jvmIdRequest(String targetId)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return jvmIdRequest(targetId, null);
+ }
+
+ public static String jvmIdRequest(String targetId, Pair credentials)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ CompletableFuture resp = new CompletableFuture<>();
+
+ JsonObject query = new JsonObject();
+ query.put(
+ "query",
+ String.format(
+ "query { targetNodes(filter: { name: \"%s\" }) { target { jvmId } } }",
+ targetId));
+ HttpRequest buffer = webClient.post("/api/v2.2/graphql");
+ if (credentials != null) {
+ buffer.putHeader(
+ "X-JMX-Authorization",
+ "Basic "
+ + Base64.getUrlEncoder()
+ .encodeToString(
+ (credentials.getLeft() + ":" + credentials.getRight())
+ .getBytes()));
+ }
+ buffer.sendJson(
+ query,
+ ar -> {
+ if (StandardSelfTest.assertRequestStatus(ar, resp)) {
+ resp.complete(
+ gson.fromJson(
+ ar.result().bodyAsString(),
+ TargetNodesQueryResponse.class));
+ }
+ });
+ TargetNodesQueryResponse response = resp.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ return response.data.targetNodes.get(0).target.jvmId;
+ }
+
+ public static String jvmIdRequest(URI serviceUri)
throws InterruptedException, ExecutionException, TimeoutException {
- return webClient
- .extensions()
- .get(
- String.format("/api/v3/targets/%d", id),
- true,
- StandardSelfTest.REQUEST_TIMEOUT_SECONDS)
- .bodyAsJsonObject()
- .getString("jvmId");
+ return jvmIdRequest(serviceUri.toString(), null);
}
- public static String jvmIdRequest(String connectUrl)
+ public static String jvmIdRequest(URI serviceUri, Pair credentials)
throws InterruptedException, ExecutionException, TimeoutException {
- return webClient
- .extensions()
- .get("/api/v3/targets", true, StandardSelfTest.REQUEST_TIMEOUT_SECONDS)
- .bodyAsJsonArray()
- .stream()
- .map(o -> (JsonObject) o)
- .filter(o -> Objects.equals(connectUrl, o.getString("connectUrl")))
- .findFirst()
- .map(o -> o.getString("jvmId"))
- .orElseThrow();
+ return jvmIdRequest(serviceUri.toString(), credentials);
+ }
+
+ static class TargetNodesQueryResponse {
+ TargetNodes data;
+ }
+
+ static class TargetNodes {
+ List targetNodes;
+ }
+
+ static class TargetNode {
+ Target target;
+ }
+
+ static class Target {
+ String jvmId;
}
}