Skip to content

Commit

Permalink
feat(diagnostic): implement diagnostics class with gc invocation oper…
Browse files Browse the repository at this point in the history
…ation (#644)

* feat(diagnostic): implement diagnostics class with gc invocation operation

* enable smoketest agent trace logging

* chore(schema): automatic update

---------

Co-authored-by: Cryostat CI <[email protected]>
  • Loading branch information
andrewazores and Cryostat CI authored Oct 1, 2024
1 parent 0bdfab1 commit 231da62
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 4 deletions.
4 changes: 3 additions & 1 deletion compose/sample_apps/gameserver-jdk11.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ services:
ENABLE_JMX: "true"
JMX_HOST: gameserver-jdk11
JMX_PORT: "7091"
JVM_OPTS: -javaagent:/opt/cryostat/agent.jar
JVM_OPTS: >-
-Dio.cryostat.agent.shaded.org.slf4j.simpleLogger.defaultLogLevel=trace
-javaagent:/opt/cryostat/agent.jar
4 changes: 3 additions & 1 deletion compose/sample_apps/gameserver-jdk17.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ services:
ENABLE_JMX: "true"
JMX_HOST: gameserver-jdk17
JMX_PORT: "7092"
JVM_OPTS: -javaagent:/opt/cryostat/agent.jar
JVM_OPTS: >-
-Dio.cryostat.agent.shaded.org.slf4j.simpleLogger.defaultLogLevel=trace
-javaagent:/opt/cryostat/agent.jar
volumes:
- ${DIR}/compose/auth_certs:/auth_certs:z
4 changes: 3 additions & 1 deletion compose/sample_apps/gameserver-jdk21.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ services:
ENABLE_JMX: "true"
JMX_HOST: gameserver-jdk21
JMX_PORT: "7093"
JVM_OPTS: -javaagent:/opt/cryostat/agent.jar
JVM_OPTS: >-
-Dio.cryostat.agent.shaded.org.slf4j.simpleLogger.defaultLogLevel=trace
-javaagent:/opt/cryostat/agent.jar
volumes:
- ${DIR}/compose/auth_certs:/auth_certs:z
3 changes: 2 additions & 1 deletion compose/sample_apps/opensearch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ services:
discovery.type: single-node
# Reference: https://opensearch.org/docs/latest/security/configuration/demo-configuration/#setting-up-a-custom-admin-password
OPENSEARCH_INITIAL_ADMIN_PASSWORD: password4Opense@rch
OPENSEARCH_JAVA_OPTS: >
OPENSEARCH_JAVA_OPTS: >-
-Dio.cryostat.agent.shaded.org.slf4j.simpleLogger.defaultLogLevel=trace
-Dcom.sun.management.jmxremote.autodiscovery=true
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9292
Expand Down
1 change: 1 addition & 0 deletions compose/sample_apps/quarkus-cryostat-agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ services:
JAVA_OPTS_APPEND: >-
-Dquarkus.http.host=0.0.0.0
-Djava.util.logging.manager=org.jboss.logmanager.LogManager
-Dio.cryostat.agent.shaded.org.slf4j.simpleLogger.defaultLogLevel=trace
-javaagent:/deployments/app/cryostat-agent.jar
-Dcom.sun.management.jmxremote.autodiscovery=false
-Dcom.sun.management.jmxremote
Expand Down
20 changes: 20 additions & 0 deletions schema/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,26 @@ info:
version: 4.0.0-snapshot
openapi: 3.0.3
paths:
/api/beta/diagnostics/targets/{targetId}/gc:
post:
parameters:
- in: path
name: targetId
required: true
schema:
format: int64
type: integer
responses:
"201":
description: Created
"401":
description: Not Authorized
"403":
description: Not Allowed
security:
- SecurityScheme: []
tags:
- Diagnostics
/api/beta/fs/recordings:
get:
responses:
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/io/cryostat/diagnostic/Diagnostics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.diagnostic;

import io.cryostat.targets.Target;
import io.cryostat.targets.TargetConnectionManager;

import io.smallrye.common.annotation.Blocking;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import org.jboss.resteasy.reactive.RestPath;

@Path("/api/beta/diagnostics/targets/{targetId}")
public class Diagnostics {

@Inject TargetConnectionManager targetConnectionManager;

@Path("/gc")
@RolesAllowed("write")
@Blocking
@POST
public void gc(@RestPath long targetId) {
targetConnectionManager.executeConnectedTask(
Target.getTargetById(targetId),
conn ->
conn.invokeMBeanOperation(
"java.lang:type=Memory", "gc", null, null, Void.class));
}
}
56 changes: 56 additions & 0 deletions src/main/java/io/cryostat/targets/AgentClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.openjdk.jmc.common.unit.IConstrainedMap;
Expand Down Expand Up @@ -108,6 +109,53 @@ Uni<MBeanMetrics> mbeanMetrics() {
.map(Unchecked.function(s -> mapper.readValue(s, MBeanMetrics.class)));
}

<T> Uni<T> invokeMBeanOperation(
String beanName,
String operation,
Object[] parameters,
String[] signature,
Class<T> returnType) {
try {
var req = new MBeanInvocationRequest(beanName, operation, parameters, signature);
return invoke(
HttpMethod.POST,
"/mbean-invoke/",
Buffer.buffer(mapper.writeValueAsBytes(req)),
BodyCodec.buffer())
.map(
Unchecked.function(
resp -> {
int statusCode = resp.statusCode();
if (HttpStatusCodeIdentifier.isSuccessCode(statusCode)) {
return resp;
} else if (statusCode == 403) {
logger.errorv(
"invokeMBeanOperation {0} ({1}) for {2} failed:"
+ " HTTP 403",
beanName, operation, getUri());
throw new ForbiddenException(
new UnsupportedOperationException(
"startRecording"));
} else {
logger.errorv(
"invokeMBeanOperation for {0} for ({1}) {2}"
+ " failed: HTTP {3}",
beanName, operation, getUri(), statusCode);
throw new AgentApiException(statusCode);
}
}))
.map(HttpResponse::bodyAsBuffer)
.map(
buff -> {
// TODO implement conditional handling based on expected returnType
return null;
});
} catch (JsonProcessingException e) {
logger.error("invokeMBeanOperation request failed", e);
return Uni.createFrom().failure(e);
}
}

Uni<IRecordingDescriptor> startRecording(StartRecordingRequest req) {
try {
return invoke(
Expand Down Expand Up @@ -568,4 +616,12 @@ public IOptionDescriptor<?> getOptionInfo(String s) {
return getOptionDescriptors().get(s);
}
}

static record MBeanInvocationRequest(
String beanName, String operation, Object[] parameters, String[] signature) {
MBeanInvocationRequest {
Objects.requireNonNull(beanName);
Objects.requireNonNull(operation);
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/io/cryostat/targets/AgentConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ public JvmIdentifier getJvmIdentifier() throws IDException, IOException {
}
}

@Override
public <T> T invokeMBeanOperation(
String beanName,
String operation,
Object[] parameters,
String[] signature,
Class<T> returnType) {
return client.invokeMBeanOperation(beanName, operation, parameters, signature, returnType)
.await()
.atMost(client.getTimeout());
}

@Override
public int getPort() {
return getUri().getPort();
Expand Down

0 comments on commit 231da62

Please sign in to comment.