diff --git a/src/main/charts/common/templates/_jmx.tpl b/src/main/charts/common/templates/_jmx.tpl index a6ecddf4e..96e5dc585 100644 --- a/src/main/charts/common/templates/_jmx.tpl +++ b/src/main/charts/common/templates/_jmx.tpl @@ -25,11 +25,36 @@ Jmx config volume mount Jmx init container */}} {{- define "common.jmx.initContainer" -}} -{{- if and .Values.monitoring.exposeJmxMetrics .Values.monitoring.fetchJmxExporterJar }} +{{- if and .Values.monitoring.exposeJmxMetrics (not .Values.monitoring.jmxExporterCustomJarLocation) }} - name: fetch-jmx-exporter - image: {{ .Values.monitoring.jmxExporterImageRepo}}:{{ .Values.monitoring.jmxExporterImageTag}} - command: ["cp"] - args: ["{{ .Values.monitoring.jmxExporterInitContainer.jmxJarLocation | default "/opt/bitnami/jmx-exporter/jmx_prometheus_javaagent.jar" }}", "{{ .Values.volumes.sharedHome.mountPath }}"] + image: curlimages/curl:latest + command: ["/bin/sh"] + args: + - -ec + - | + AGENT_PATH="{{ .Values.volumes.sharedHome.mountPath }}/jmx_prometheus_javaagent.jar" + VERSION="{{ .Values.monitoring.jmxExporter.version }}" + BASE_URL="{{ .Values.monitoring.jmxExporter.mavenBaseUrl }}" + EXPECTED_SHA256="{{ .Values.monitoring.jmxExporter.sha256 }}" + + # Download the agent + curl -L "${BASE_URL}/${VERSION}/jmx_prometheus_javaagent-${VERSION}.jar" -o $AGENT_PATH + + # Verify SHA256 + echo "${EXPECTED_SHA256} ${AGENT_PATH}" | sha256sum -c - + + # Set permissions + chmod 644 $AGENT_PATH + + # Verify JAR structure + if ! unzip -l $AGENT_PATH | grep -q "META-INF/MANIFEST.MF"; then + echo "Invalid JAR file" + exit 1 + fi + if ! unzip -l $AGENT_PATH | grep -q "JavaAgent.class"; then + echo "Not a valid Java agent JAR" + exit 1 + fi {{- if .Values.monitoring.jmxExporterInitContainer.resources }} resources: {{- with .Values.monitoring.jmxExporterInitContainer.resources }} diff --git a/src/main/charts/jira/values.yaml b/src/main/charts/jira/values.yaml index 957e866c7..0a306b118 100644 --- a/src/main/charts/jira/values.yaml +++ b/src/main/charts/jira/values.yaml @@ -998,18 +998,12 @@ monitoring: # -- Expose JMX metrics with jmx_exporter https://github.com/prometheus/jmx_exporter # - exposeJmxMetrics: false + exposeJmxMetrics: true - # -- JMX exporter init container configuration + # -- JMX exporter init container configuration # jmxExporterInitContainer: - - # -- The location of the JMX exporter jarfile in the JMX exporter image - # Leave blank for default bitnami image - # - jmxJarLocation: - - # -- Whether to run JMX exporter init container as root to copy JMX exporter binary to shared home volume. + # -- Whether to run JMX exporter init container as root to set permissions # Set to false if running containers as root is not allowed in the cluster. # runAsRoot: true @@ -1033,18 +1027,15 @@ monitoring: # jmxServiceAnnotations: {} - # -- Fetch jmx_exporter jar from the image. If set to false make sure to manually copy the jar - # to shared home and provide an absolute path in jmxExporterCustomJarLocation - # - fetchJmxExporterJar: true - - # -- Image repository with jmx_exporter jar - # - jmxExporterImageRepo: bitnami/jmx-exporter - - # -- Image tag to be used to pull jmxExporterImageRepo + # -- Maven Central configuration for JMX exporter # - jmxExporterImageTag: 0.18.0 + jmxExporter: + # -- Version of JMX exporter to use + version: 0.18.0 + # -- Maven Central base URL + mavenBaseUrl: https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent + # -- SHA256 checksum for verification + sha256: d3ba4ce8eccd3461463358c5e19f8becb99ed949f159c37c850e53b89b560f97 # -- Port number on which metrics will be available # @@ -1060,15 +1051,30 @@ monitoring: # -- Custom JMX config with the rules # - jmxExporterCustomConfig: {} - # rules: - # - pattern: ".*" + jmxExporterCustomConfig: + rules: + - pattern: "java.lang.*" + name: jvm_memory_heap_$3 + attrNameSnakeCase: true + - pattern: "java.lang.*" + name: jvm_memory_nonheap_$3 + attrNameSnakeCase: true + - pattern: "java.lang<.*>.*" + name: jvm_gc_$1_$2 + attrNameSnakeCase: true + - pattern: "Catalina(.*)" + name: tomcat_threadpool_$2 + labels: + pool: $1 + - pattern: "com.atlassian.jira(.*)" + name: jira_thread_$1 + attrNameSnakeCase: true serviceMonitor: # -- Create ServiceMonitor to start scraping metrics. ServiceMonitor CRD needs to be created in advance. # - create: false + create: true # -- ServiceMonitorSelector of the prometheus instance. # diff --git a/src/test/java/test/JmxMetricsTest.java b/src/test/java/test/JmxMetricsTest.java index bc4cf331f..498eaa340 100644 --- a/src/test/java/test/JmxMetricsTest.java +++ b/src/test/java/test/JmxMetricsTest.java @@ -41,9 +41,9 @@ void expose_jmx_metrics_enabled_init_container(Product product) throws Exception StatefulSet statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); // assert jmx_exporter init container - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("image")).hasTextEqualTo("bitnami/jmx-exporter:0.18.0"); - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("command").get(0)).hasTextEqualTo("cp"); - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(0)).hasTextEqualTo("/opt/bitnami/jmx-exporter/jmx_prometheus_javaagent.jar"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("image")).hasTextEqualTo("curlimages/curl:latest"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("command").get(0)).hasTextEqualTo("/bin/sh"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(0)).hasTextEqualTo("-ec"); assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(1)).hasTextEqualTo(sharedHomePath); assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("volumeMounts").get(0).path("mountPath")).hasTextEqualTo(sharedHomePath); @@ -164,14 +164,44 @@ void expose_jmx_metrics_enabled_custom_vol_paths(Product product) throws Excepti void expose_jmx_metrics_enabled_custom_init_container(Product product) throws Exception { final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( "monitoring.exposeJmxMetrics", "true", - "monitoring.jmxExporterImageRepo", "myregistry/myrepo", - "monitoring.jmxExporterImageTag", "0.17.0" + "monitoring.jmxExporter.version", "0.17.0", + "monitoring.jmxExporter.sha256", "custom_sha256" )); StatefulSet statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); // assert jmx_exporter init container - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("image")).hasTextEqualTo("myregistry/myrepo:0.17.0"); + var initContainer = statefulSet.getInitContainer("fetch-jmx-exporter").get(); + assertThat(initContainer.path("image")).hasTextEqualTo("curlimages/curl:latest"); + + // Verify custom version is used + assertThat(initContainer.path("args").get(1)).hasTextContaining("VERSION=\"0.17.0\""); + assertThat(initContainer.path("args").get(1)).hasTextContaining("EXPECTED_SHA256=\"custom_sha256\""); + } + + @ParameterizedTest + @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE) + void expose_jmx_metrics_maven_central_download(Product product) throws Exception { + final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of( + "monitoring.exposeJmxMetrics", "true", + "monitoring.jmxExporter.version", "0.18.0", + "monitoring.jmxExporter.sha256", "d3ba4ce8eccd3461463358c5e19f8becb99ed949f159c37c850e53b89b560f97" + )); + + StatefulSet statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); + var initContainer = statefulSet.getInitContainer("fetch-jmx-exporter").get(); + + // Verify init container setup + assertThat(initContainer.path("image")).hasTextEqualTo("curlimages/curl:latest"); + assertThat(initContainer.path("command").get(0)).hasTextEqualTo("/bin/sh"); + assertThat(initContainer.path("args").get(0)).hasTextEqualTo("-ec"); + + // Verify script content + assertThat(initContainer.path("args").get(1)).hasTextContaining("curl -L"); + assertThat(initContainer.path("args").get(1)).hasTextContaining("sha256sum -c"); + assertThat(initContainer.path("args").get(1)).hasTextContaining("chmod 644"); + assertThat(initContainer.path("args").get(1)).hasTextContaining("META-INF/MANIFEST.MF"); + assertThat(initContainer.path("args").get(1)).hasTextContaining("JavaAgent.class"); } @ParameterizedTest @@ -250,9 +280,9 @@ void expose_jmx_metrics_bitbucket_mesh(Product product) throws Exception { StatefulSet statefulSet = resources.getStatefulSet(product.getHelmReleaseName() + "-mesh"); // assert jmx_exporter init container - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("image")).hasTextEqualTo("bitnami/jmx-exporter:0.18.0"); - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("command").get(0)).hasTextEqualTo("cp"); - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(0)).hasTextEqualTo("/opt/bitnami/jmx-exporter/jmx_prometheus_javaagent.jar"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("image")).hasTextEqualTo("curlimages/curl:latest"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("command").get(0)).hasTextEqualTo("/bin/sh"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(0)).hasTextEqualTo("-ec"); assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(1)).hasTextEqualTo(localHomePath); assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("volumeMounts").get(0).path("mountPath")).hasTextEqualTo(localHomePath); @@ -286,9 +316,9 @@ void expose_jmx_metrics_enabled_bitbucket_mirror(Product product) throws Excepti StatefulSet statefulSet = resources.getStatefulSet(product.getHelmReleaseName()); // assert jmx_exporter init container - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("image")).hasTextEqualTo("bitnami/jmx-exporter:0.18.0"); - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("command").get(0)).hasTextEqualTo("cp"); - assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(0)).hasTextEqualTo("/opt/bitnami/jmx-exporter/jmx_prometheus_javaagent.jar"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("image")).hasTextEqualTo("curlimages/curl:latest"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("command").get(0)).hasTextEqualTo("/bin/sh"); + assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(0)).hasTextEqualTo("-ec"); assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("args").get(1)).hasTextEqualTo(sharedHomePath); assertThat(statefulSet.getInitContainer("fetch-jmx-exporter").get().path("volumeMounts").get(0).path("mountPath")).hasTextEqualTo(sharedHomePath); diff --git a/src/test/resources/expected_helm_output/jira/output.yaml b/src/test/resources/expected_helm_output/jira/output.yaml index ead800f02..b346c0a65 100644 --- a/src/test/resources/expected_helm_output/jira/output.yaml +++ b/src/test/resources/expected_helm_output/jira/output.yaml @@ -262,15 +262,16 @@ data: useHelmReleaseNameAsContainerName: false monitoring: exposeJmxMetrics: true - fetchJmxExporterJar: true grafana: createDashboards: false dashboardAnnotations: {} dashboardLabels: {} jmxExporterCustomConfig: {} jmxExporterCustomJarLocation: null - jmxExporterImageRepo: bitnami/jmx-exporter - jmxExporterImageTag: 0.18.0 + jmxExporter: + version: 0.18.0 + mavenBaseUrl: https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent + sha256: d3ba4ce8eccd3461463358c5e19f8becb99ed949f159c37c850e53b89b560f97 jmxExporterInitContainer: customSecurityContext: {} jmxJarLocation: null @@ -453,9 +454,34 @@ spec: mountPath: "/shared-home" command: ["sh", "-c", "(chgrp 2001 /shared-home; chmod g+w /shared-home)"] - name: fetch-jmx-exporter - image: bitnami/jmx-exporter:0.18.0 - command: ["cp"] - args: ["/opt/bitnami/jmx-exporter/jmx_prometheus_javaagent.jar", "/var/atlassian/application-data/shared-home"] + image: curlimages/curl:latest + command: ["/bin/sh"] + args: + - -ec + - | + AGENT_PATH="/var/atlassian/application-data/shared-home/jmx_prometheus_javaagent.jar" + VERSION="0.18.0" + BASE_URL="https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent" + EXPECTED_SHA256="d3ba4ce8eccd3461463358c5e19f8becb99ed949f159c37c850e53b89b560f97" + + # Download the agent + curl -L "${BASE_URL}/${VERSION}/jmx_prometheus_javaagent-${VERSION}.jar" -o $AGENT_PATH + + # Verify SHA256 + echo "${EXPECTED_SHA256} ${AGENT_PATH}" | sha256sum -c - + + # Set permissions + chmod 644 $AGENT_PATH + + # Verify JAR structure + if ! unzip -l $AGENT_PATH | grep -q "META-INF/MANIFEST.MF"; then + echo "Invalid JAR file" + exit 1 + fi + if ! unzip -l $AGENT_PATH | grep -q "JavaAgent.class"; then + echo "Not a valid Java agent JAR" + exit 1 + fi securityContext: runAsUser: 0 volumeMounts: