From f36898ba7b80670183192f111096dc9212643f23 Mon Sep 17 00:00:00 2001 From: jmatsuok Date: Tue, 7 Apr 2026 20:41:34 -0400 Subject: [PATCH] Support diagnostic operations with automated rules --- schema/openapi.yaml | 12 ++++++++++++ src/main/java/io/cryostat/rules/Rule.java | 4 ++++ .../java/io/cryostat/rules/RuleExecutor.java | 19 +++++++++++++++++++ src/main/java/io/cryostat/rules/Rules.java | 12 +++++++++++- .../db/migration/V4.0.0__cryostat.sql | 2 ++ .../db/migration/V4.2.0__cryostat.sql | 2 ++ 6 files changed, 50 insertions(+), 1 deletion(-) diff --git a/schema/openapi.yaml b/schema/openapi.yaml index 8089e951c..6591ed2ad 100644 --- a/schema/openapi.yaml +++ b/schema/openapi.yaml @@ -658,6 +658,8 @@ components: eventSpecifier: pattern: \S type: string + heapDump: + type: boolean id: format: int64 type: integer @@ -684,6 +686,8 @@ components: format: int32 minimum: 0 type: integer + threadDump: + type: boolean required: - name - description @@ -2639,6 +2643,8 @@ paths: type: boolean eventSpecifier: type: string + heapDump: + type: boolean initialDelaySeconds: format: int32 type: integer @@ -2659,6 +2665,8 @@ paths: preservedArchives: format: int32 type: integer + threadDump: + type: boolean type: object multipart/form-data: schema: @@ -2672,6 +2680,8 @@ paths: type: boolean eventSpecifier: type: string + heapDump: + type: boolean initialDelaySeconds: format: int32 type: integer @@ -2692,6 +2702,8 @@ paths: preservedArchives: format: int32 type: integer + threadDump: + type: boolean type: object required: true responses: diff --git a/src/main/java/io/cryostat/rules/Rule.java b/src/main/java/io/cryostat/rules/Rule.java index 45e199445..fa4cfc260 100644 --- a/src/main/java/io/cryostat/rules/Rule.java +++ b/src/main/java/io/cryostat/rules/Rule.java @@ -102,6 +102,10 @@ public class Rule extends PanacheEntity { public boolean enabled; + public boolean heapDump; + + public boolean threadDump; + public String getName() { return this.name; } diff --git a/src/main/java/io/cryostat/rules/RuleExecutor.java b/src/main/java/io/cryostat/rules/RuleExecutor.java index d256371c3..12997a9e4 100644 --- a/src/main/java/io/cryostat/rules/RuleExecutor.java +++ b/src/main/java/io/cryostat/rules/RuleExecutor.java @@ -23,12 +23,16 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; import io.cryostat.expressions.MatchExpressionEvaluator; import io.cryostat.libcryostat.templates.Template; import io.cryostat.libcryostat.templates.TemplateType; import io.cryostat.recordings.ActiveRecording; +import io.cryostat.recordings.LongRunningRequestGenerator; +import io.cryostat.recordings.LongRunningRequestGenerator.HeapDumpRequest; +import io.cryostat.recordings.LongRunningRequestGenerator.ThreadDumpRequest; import io.cryostat.recordings.RecordingHelper; import io.cryostat.recordings.RecordingHelper.RecordingOptions; import io.cryostat.recordings.RecordingHelper.RecordingReplace; @@ -42,6 +46,7 @@ import io.quarkus.runtime.ShutdownEvent; import io.quarkus.vertx.ConsumeEvent; import io.smallrye.mutiny.Uni; +import io.vertx.mutiny.core.eventbus.EventBus; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; @@ -79,6 +84,9 @@ public class RuleExecutor { @Inject MatchExpressionEvaluator evaluator; @Inject Scheduler quartz; + @Inject LongRunningRequestGenerator generator; + @Inject EventBus eventBus; + void onStop(@Observes ShutdownEvent evt) throws SchedulerException { quartz.shutdown(); } @@ -103,6 +111,17 @@ Uni onMessage(ActivationAttempt attempt) { if (priorRecording.isPresent()) { recordingHelper.stopRecording(priorRecording.get()).await().indefinitely(); } + if (rule.heapDump) { + HeapDumpRequest request = + new HeapDumpRequest(UUID.randomUUID().toString(), target.id); + eventBus.publish(LongRunningRequestGenerator.HEAP_DUMP_REQUEST_ADDRESS, request); + } + if (rule.threadDump) { + ThreadDumpRequest request = + new ThreadDumpRequest( + UUID.randomUUID().toString(), target.id, "threadPrint"); + eventBus.publish(LongRunningRequestGenerator.THREAD_DUMP_ADDRESS, request); + } var labels = new HashMap<>(rule.metadata.labels()); labels.put(RULE_LABEL_KEY, rule.name); ActiveRecording recording = null; diff --git a/src/main/java/io/cryostat/rules/Rules.java b/src/main/java/io/cryostat/rules/Rules.java index 70903e2d4..c15012182 100644 --- a/src/main/java/io/cryostat/rules/Rules.java +++ b/src/main/java/io/cryostat/rules/Rules.java @@ -207,6 +207,12 @@ public Rule update( if (body.containsKey("metadata")) { rule.metadata = body.getJsonObject("metadata").mapTo(Metadata.class); } + if (body.containsKey("heapDump")) { + rule.heapDump = body.getBoolean("heapDump"); + } + if (body.containsKey("threadDump")) { + rule.threadDump = body.getBoolean("threadDump"); + } rule.persist(); @@ -229,7 +235,9 @@ public RestResponse create( @RestForm int maxAgeSeconds, @RestForm int maxSizeBytes, @RestForm("metadata") Optional rawMetadata, - @RestForm boolean enabled) + @RestForm boolean enabled, + @RestForm boolean heapDump, + @RestForm boolean threadDump) throws JsonMappingException, JsonProcessingException { MatchExpression expr = new MatchExpression(matchExpression); expr.persist(); @@ -243,6 +251,8 @@ public RestResponse create( rule.preservedArchives = preservedArchives; rule.maxAgeSeconds = maxAgeSeconds; rule.maxSizeBytes = maxSizeBytes; + rule.heapDump = heapDump; + rule.threadDump = threadDump; if (rawMetadata.isPresent()) { rule.metadata = mapper.readValue(rawMetadata.get(), Metadata.class); } diff --git a/src/main/resources/db/migration/V4.0.0__cryostat.sql b/src/main/resources/db/migration/V4.0.0__cryostat.sql index 8be4df1c5..54f24977f 100644 --- a/src/main/resources/db/migration/V4.0.0__cryostat.sql +++ b/src/main/resources/db/migration/V4.0.0__cryostat.sql @@ -73,6 +73,8 @@ name text unique check (char_length(name) < 255), preservedArchives integer not null, matchExpression bigint unique, + threadDump boolean not null, + heapDump boolean not null, primary key (id) ); diff --git a/src/main/resources/db/migration/V4.2.0__cryostat.sql b/src/main/resources/db/migration/V4.2.0__cryostat.sql index fe60b3534..8c9c12b33 100644 --- a/src/main/resources/db/migration/V4.2.0__cryostat.sql +++ b/src/main/resources/db/migration/V4.2.0__cryostat.sql @@ -248,6 +248,8 @@ CREATE TABLE Rule_AUD ( maxSizeBytes INTEGER, metadata TEXT, enabled BOOLEAN, + threadDump BOOLEAN, + heapDump BOOLEAN, PRIMARY KEY (id, REV), FOREIGN KEY (REV) REFERENCES REVINFO (REV), FOREIGN KEY (REVEND) REFERENCES REVINFO (REV)