Skip to content

Add /enableMonitoring API for JMX Agent Configuration#1926

Open
shreyabiradar07 wants to merge 2 commits into
kruize:runtimes-monitoringfrom
shreyabiradar07:enable_jmx_monitoring
Open

Add /enableMonitoring API for JMX Agent Configuration#1926
shreyabiradar07 wants to merge 2 commits into
kruize:runtimes-monitoringfrom
shreyabiradar07:enable_jmx_monitoring

Conversation

@shreyabiradar07

@shreyabiradar07 shreyabiradar07 commented May 27, 2026

Copy link
Copy Markdown
Contributor

Description

Introduces a new REST API endpoint to automatically configure JMX monitoring for Java workloads in Kubernetes.

Changes

  • New /enableMonitoring POST endpoint for automated JMX agent setup
  • Configures JMX exporter via JAVA_OPTS with ConfigMap-based configuration
  • Creates ConfigMap, Service, and ServiceMonitor for Prometheus scraping
  • Added RBAC permissions for ConfigMap, Service, and ServiceMonitor resources
  • Enhanced Kubernetes service layer with JMX configuration methods
  • Updated hotspot layer configs to detect JMX exporter metrics
  • New API objects: EnableJMXAgentAPIObject and EnableJMXAgentResponseAPIObject

Fixes # (issue)

Type of change

  • Bug fix
  • New feature
  • Docs update
  • Breaking change (What changes might users need to make in their application due to this PR?)
  • Requires DB changes

How has this been tested?

Please describe the tests that were run to verify your changes and steps to reproduce. Please specify any test configuration required.

  • New Test X
  • Functional testsuite

Test Configuration

  • Kubernetes clusters tested on:

Checklist 🎯

  • Followed coding guidelines
  • Comments added
  • Dependent changes merged
  • Documentation updated
  • Tests added or updated

Additional information

Include any additional information such as links, test results, screenshots here

Summary by Sourcery

Introduce an /enableMonitoring REST endpoint that configures JMX exporter-based monitoring for Java Kubernetes workloads, wiring it into the analyzer service and Kubernetes integration layer.

New Features:

  • Add /enableMonitoring servlet endpoint to enable JMX exporter monitoring on Java workloads via a JSON API.
  • Automatically create and manage JMX exporter ConfigMaps, init containers, ports, Services, and ServiceMonitors so Prometheus can scrape metrics.

Enhancements:

  • Extend the KubernetesServices abstraction with a deployment replacement operation used to roll out updated monitoring configuration.

Deployment:

  • Grant RBAC permissions for ConfigMaps, Services, and ServiceMonitor resources required by the monitoring enablement flow.

Signed-off-by: Shreya Biradar <shbirada@ibm.com>
@sourcery-ai

sourcery-ai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a new /enableMonitoring REST endpoint that programmatically enables JMX exporter–based monitoring on Java workloads in Kubernetes by mutating Deployments, creating ConfigMaps/Services/ServiceMonitors, and wiring in the JMX agent via JAVA_OPTS, along with the necessary RBAC and API objects.

Sequence diagram for /enableMonitoring JMX setup flow

sequenceDiagram
    actor User
    participant EnableMonitoring
    participant KubernetesServices

    User->>EnableMonitoring: HTTP POST /enableMonitoring
    EnableMonitoring->>EnableMonitoring: doPost
    EnableMonitoring->>EnableMonitoring: processContainer
    EnableMonitoring->>KubernetesServices: getDeploymentBy(namespace, workloadName)
    alt deployment found and workload_type == deployment
        EnableMonitoring->>EnableMonitoring: enableJMXMonitoring(kubernetesServices, deployment, namespace, workloadName, containerName)
        EnableMonitoring->>EnableMonitoring: createJMXConfigMap(client, namespace, configMapName)
        EnableMonitoring->>KubernetesServices: replaceDeployment(namespace, workloadName, deployment)
        alt replaceDeployment successful
            EnableMonitoring->>KubernetesServices: restartDeployment(namespace, workloadName)
            EnableMonitoring->>EnableMonitoring: updateServiceWithJMXPort(client, namespace, workloadName)
            EnableMonitoring->>EnableMonitoring: createServiceMonitor(client, namespace, workloadName)
        else replaceDeployment failed
            EnableMonitoring-->>EnableMonitoring: mark container failure
        end
    else invalid workload or deployment missing
        EnableMonitoring-->>EnableMonitoring: mark container failure
    end
    EnableMonitoring-->>User: JSON response with successful/failed lists
Loading

File-Level Changes

Change Details Files
Expose a new /enableMonitoring servlet endpoint in the analyzer to drive JMX monitoring enablement.
  • Registers EnableMonitoring servlet on the ENABLE_MONITORING path in the Analyzer servlet configuration
  • Defines ENABLE_MONITORING path constant in ServerContext
src/main/java/com/autotune/analyzer/Analyzer.java
src/main/java/com/autotune/utils/ServerContext.java
Implement the EnableMonitoring servlet that configures JMX exporter for targeted Kubernetes workloads.
  • Accepts a JSON payload describing target containers and validates required fields
  • Looks up Deployments via KubernetesServices, mutates pod specs to add init container, volumes, ports, and JAVA_OPTS for JMX exporter
  • Creates or updates a ConfigMap containing JMX exporter config and the JAR binary (downloaded from Maven)
  • Updates the associated Service to expose the JMX metrics port and creates/patches a ServiceMonitor custom resource for Prometheus scraping
  • Aggregates per-container success/failure into a structured JSON response and sets HTTP status (200/400/207/500) accordingly
src/main/java/com/autotune/analyzer/services/EnableMonitoring.java
Extend the Kubernetes service abstraction to support replacing Deployments mutated for monitoring.
  • Adds replaceDeployment method to the KubernetesServices interface to support full Deployment updates used by the monitoring flow
src/main/java/com/autotune/common/target/kubernetes/service/KubernetesServices.java
Define API objects describing JMX agent enablement requests and responses.
  • Introduces EnableJMXAgentAPIObject to capture namespace/workload/container and JMX connection settings
  • Introduces EnableJMXAgentResponseAPIObject to return status, message, workload identity, JMX port, and JMX service URL
src/main/java/com/autotune/analyzer/serviceObjects/EnableJMXAgentAPIObject.java
src/main/java/com/autotune/analyzer/serviceObjects/EnableJMXAgentResponseAPIObject.java
Grant RBAC permissions required to manage JMX monitoring resources on OpenShift CRC installations.
  • Extends ClusterRole to allow CRUD on ConfigMaps used for JMX exporter configuration
  • Extends ClusterRole to allow read/update/patch on Services that expose the JMX metrics port
  • Extends ClusterRole to allow CRUD on ServiceMonitor custom resources in monitoring.coreos.com API group
manifests/crc/default-db-included-installation/openshift/kruize-crc-openshift.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Signed-off-by: Shreya Biradar <shbirada@ibm.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 5 issues, and left some high level feedback:

  • The EnableMonitoring servlet has grown very large and mixes HTTP handling, JSON parsing, K8s orchestration, and Prometheus wiring in a single class; consider extracting the Kubernetes/JMX configuration logic into dedicated service/utility classes to keep the servlet focused on request/response handling.
  • The servlet currently downloads the JMX exporter JAR from Maven Central on request and embeds it as binaryData in a ConfigMap, which introduces network latency/failure modes and large ConfigMaps; it would be more robust to source the JAR from a pre-baked image, a configurable internal URL, or a pre-created ConfigMap/volume.
  • The new EnableJMXAgentAPIObject and EnableJMXAgentResponseAPIObject types are not used by the /enableMonitoring endpoint, which still manually builds/parses JSON; consider either wiring these POJOs into the API layer for type safety and consistency or removing them to avoid dead code.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `EnableMonitoring` servlet has grown very large and mixes HTTP handling, JSON parsing, K8s orchestration, and Prometheus wiring in a single class; consider extracting the Kubernetes/JMX configuration logic into dedicated service/utility classes to keep the servlet focused on request/response handling.
- The servlet currently downloads the JMX exporter JAR from Maven Central on request and embeds it as `binaryData` in a ConfigMap, which introduces network latency/failure modes and large ConfigMaps; it would be more robust to source the JAR from a pre-baked image, a configurable internal URL, or a pre-created ConfigMap/volume.
- The new `EnableJMXAgentAPIObject` and `EnableJMXAgentResponseAPIObject` types are not used by the `/enableMonitoring` endpoint, which still manually builds/parses JSON; consider either wiring these POJOs into the API layer for type safety and consistency or removing them to avoid dead code.

## Individual Comments

### Comment 1
<location path="src/main/java/com/autotune/analyzer/services/EnableMonitoring.java" line_range="228" />
<code_context>
+            if (!initContainerExists) {
+                Container initContainer = new Container();
+                initContainer.setName("jmx-exporter-init");
+                initContainer.setImage("busybox:latest");
+                initContainer.setCommand(List.of("sh", "-c",
+                    "cp /config/" + JMX_JAR_FILE + " /jmx-exporter/ && " +
</code_context>
<issue_to_address>
**🚨 issue (security):** Avoid using the `latest` tag for the init container image to ensure reproducibility and reduce supply-chain risk.

Using `busybox:latest` makes deployments non-deterministic and may pull unvetted images, which complicates security reviews and SBOM generation. Please pin to a specific trusted tag (for example, `busybox:1.36.1`) or an internal, controlled base image.
</issue_to_address>

### Comment 2
<location path="src/main/java/com/autotune/analyzer/services/EnableMonitoring.java" line_range="443-452" />
<code_context>
+    private boolean createJMXConfigMap(KubernetesClient client, String namespace, String configMapName) {
</code_context>
<issue_to_address>
**🚨 issue (security):** Downloading the JMX exporter JAR at runtime from Maven Central is fragile and poses security and operability concerns.

This downloads the agent JAR over the network on each enablement, which (1) breaks in air‑gapped/restricted clusters, (2) adds a hard runtime dependency on Maven Central, (3) increases startup latency, and (4) expands the supply‑chain attack surface. Prefer bundling the JAR into a known image (init/sidecar) or mounting it from a pre-provisioned ConfigMap/Secret created at install time, so the JAR isn’t fetched on each request and doesn’t depend on live external endpoints.
</issue_to_address>

### Comment 3
<location path="src/main/java/com/autotune/analyzer/services/EnableMonitoring.java" line_range="204" />
<code_context>
+        KubernetesClient client = null;
+        try {
+            // Create Kubernetes client
+            client = new DefaultKubernetesClient();
+            
+            // Step 1: Create ConfigMap with JMX exporter configuration
</code_context>
<issue_to_address>
**suggestion (performance):** Creating a new `DefaultKubernetesClient` inside `enableJMXMonitoring` for each call is unnecessary overhead.

This creates and closes a new `DefaultKubernetesClient` for every container, despite an existing `KubernetesServices` instance at the servlet level. This adds avoidable connection setup/teardown and duplicates configuration. Please reuse a shared client (e.g., from `KubernetesServicesImpl`) or inject a client so a single long‑lived instance is used per servlet rather than per call.

Suggested implementation:

```java
    private boolean enableJMXMonitoring(KubernetesServices kubernetesServices, Deployment deployment,
                                       String namespace, String workloadName, String containerName) {
        KubernetesClient client = kubernetesServices.getClient();
        try {
            // Step 1: Create ConfigMap with JMX exporter configuration

```

1. Ensure that the `KubernetesServices` interface (and its implementation, e.g. `KubernetesServicesImpl`) exposes a `getClient()` method returning the shared `KubernetesClient` instance.
2. Confirm that the shared `KubernetesClient` lifecycle (creation/closing) is managed at the servlet/application level, not inside `enableJMXMonitoring`, and that this method does not close the client.
3. If the existing accessor method on `KubernetesServices` uses a different name (e.g. `getKubernetesClient()`), adjust the call accordingly.
</issue_to_address>

### Comment 4
<location path="src/main/java/com/autotune/analyzer/services/EnableMonitoring.java" line_range="553" />
<code_context>
+            LOGGER.info("Found Service {} for workload {} in namespace {}", serviceName, workloadName, namespace);
+            
+            // Check if JMX metrics port already exists
+            List<ServicePort> ports = service.getSpec().getPorts();
+            boolean jmxPortExists = ports.stream()
+                .anyMatch(p -> p.getName() != null && p.getName().equals(JMX_PROMETHEUS_PORT_NAME));
</code_context>
<issue_to_address>
**issue (bug_risk):** `service.getSpec().getPorts()` can be null, which would cause a NullPointerException in `updateServiceWithJMXPort`.

If the Service has no ports, `service.getSpec().getPorts()` can be null, so `ports.stream()` and later `ports.add(jmxPort)` will throw an NPE. Initialize `ports` when null (e.g. `ports = new ArrayList<>()` and set it back on the spec) before streaming or adding entries.
</issue_to_address>

### Comment 5
<location path="manifests/crc/default-db-included-installation/openshift/kruize-crc-openshift.yaml" line_range="88-93" />
<code_context>
   - apiGroups: [ "" ]
     resources: [ "namespaces" ]
     verbs: [ "get", "list" ]
+  - apiGroups: [ "" ]
+    resources: [ "configmaps" ]
+    verbs: [ "create", "get", "list", "update", "patch", "delete" ]
+  - apiGroups: [ "" ]
+    resources: [ "services" ]
</code_context>
<issue_to_address>
**🚨 question (security):** The new RBAC rules grant broad permissions (including delete) on ConfigMaps and ServiceMonitors; consider tightening to least privilege.

Given this flow only needs to manage a small set of our own ConfigMaps/Services/ServiceMonitors, broad cluster-wide CRUD (including delete) significantly enlarges the impact of any compromise. Please narrow the permissions where possible (e.g., drop `delete` if not strictly needed, scope to specific namespaces, or use `resourceNames`) to better follow least-privilege.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

if (!initContainerExists) {
Container initContainer = new Container();
initContainer.setName("jmx-exporter-init");
initContainer.setImage("busybox:latest");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Avoid using the latest tag for the init container image to ensure reproducibility and reduce supply-chain risk.

Using busybox:latest makes deployments non-deterministic and may pull unvetted images, which complicates security reviews and SBOM generation. Please pin to a specific trusted tag (for example, busybox:1.36.1) or an internal, controlled base image.

Comment on lines +443 to +452
private boolean createJMXConfigMap(KubernetesClient client, String namespace, String configMapName) {
try {
// Default JMX exporter configuration
String jmxConfig = "lowercaseOutputName: true\n" +
"lowercaseOutputLabelNames: true\n" +
"rules:\n" +
"- pattern: \".*\"\n";

// Download JMX exporter JAR
LOGGER.info("Downloading JMX exporter JAR from {}", JMX_EXPORTER_DOWNLOAD_URL);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Downloading the JMX exporter JAR at runtime from Maven Central is fragile and poses security and operability concerns.

This downloads the agent JAR over the network on each enablement, which (1) breaks in air‑gapped/restricted clusters, (2) adds a hard runtime dependency on Maven Central, (3) increases startup latency, and (4) expands the supply‑chain attack surface. Prefer bundling the JAR into a known image (init/sidecar) or mounting it from a pre-provisioned ConfigMap/Secret created at install time, so the JAR isn’t fetched on each request and doesn’t depend on live external endpoints.

KubernetesClient client = null;
try {
// Create Kubernetes client
client = new DefaultKubernetesClient();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Creating a new DefaultKubernetesClient inside enableJMXMonitoring for each call is unnecessary overhead.

This creates and closes a new DefaultKubernetesClient for every container, despite an existing KubernetesServices instance at the servlet level. This adds avoidable connection setup/teardown and duplicates configuration. Please reuse a shared client (e.g., from KubernetesServicesImpl) or inject a client so a single long‑lived instance is used per servlet rather than per call.

Suggested implementation:

    private boolean enableJMXMonitoring(KubernetesServices kubernetesServices, Deployment deployment,
                                       String namespace, String workloadName, String containerName) {
        KubernetesClient client = kubernetesServices.getClient();
        try {
            // Step 1: Create ConfigMap with JMX exporter configuration
  1. Ensure that the KubernetesServices interface (and its implementation, e.g. KubernetesServicesImpl) exposes a getClient() method returning the shared KubernetesClient instance.
  2. Confirm that the shared KubernetesClient lifecycle (creation/closing) is managed at the servlet/application level, not inside enableJMXMonitoring, and that this method does not close the client.
  3. If the existing accessor method on KubernetesServices uses a different name (e.g. getKubernetesClient()), adjust the call accordingly.

LOGGER.info("Found Service {} for workload {} in namespace {}", serviceName, workloadName, namespace);

// Check if JMX metrics port already exists
List<ServicePort> ports = service.getSpec().getPorts();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): service.getSpec().getPorts() can be null, which would cause a NullPointerException in updateServiceWithJMXPort.

If the Service has no ports, service.getSpec().getPorts() can be null, so ports.stream() and later ports.add(jmxPort) will throw an NPE. Initialize ports when null (e.g. ports = new ArrayList<>() and set it back on the spec) before streaming or adding entries.

Comment on lines 88 to +93
- apiGroups: [ "" ]
resources: [ "namespaces" ]
verbs: [ "get", "list" ]
- apiGroups: [ "" ]
resources: [ "configmaps" ]
verbs: [ "create", "get", "list", "update", "patch", "delete" ]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 question (security): The new RBAC rules grant broad permissions (including delete) on ConfigMaps and ServiceMonitors; consider tightening to least privilege.

Given this flow only needs to manage a small set of our own ConfigMaps/Services/ServiceMonitors, broad cluster-wide CRUD (including delete) significantly enlarges the impact of any compromise. Please narrow the permissions where possible (e.g., drop delete if not strictly needed, scope to specific namespaces, or use resourceNames) to better follow least-privilege.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant