Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions jmx-scraper/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ testing {
dependencies {
implementation("org.testcontainers:junit-jupiter")
implementation("org.slf4j:slf4j-simple")
implementation("com.linecorp.armeria:armeria-junit5")
implementation("com.linecorp.armeria:armeria-grpc")
implementation("io.opentelemetry.proto:opentelemetry-proto:0.20.0-alpha")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.jmxscraper;

import static org.assertj.core.api.Assertions.assertThat;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.MountableFile;

/** Test container that allows to execute {@link JmxScraper} in an isolated container */
public class JmxScraperContainer extends GenericContainer<JmxScraperContainer> {

private final String endpoint;
private final Set<String> targetSystems;
private String serviceUrl;
private int intervalMillis;
private final Set<String> customYaml;

public JmxScraperContainer(String otlpEndpoint) {
super("openjdk:8u272-jre-slim");

String scraperJarPath = System.getProperty("shadow.jar.path");
assertThat(scraperJarPath).isNotNull();

this.withCopyFileToContainer(MountableFile.forHostPath(scraperJarPath), "/scraper.jar")
.waitingFor(
Wait.forLogMessage(".*JMX scraping started.*", 1)
.withStartupTimeout(Duration.ofSeconds(10)));

this.endpoint = otlpEndpoint;
this.targetSystems = new HashSet<>();
this.customYaml = new HashSet<>();
this.intervalMillis = 1000;
}

@CanIgnoreReturnValue
public JmxScraperContainer withTargetSystem(String targetSystem) {
targetSystems.add(targetSystem);
return this;
}

@CanIgnoreReturnValue
public JmxScraperContainer withIntervalMillis(int intervalMillis) {
this.intervalMillis = intervalMillis;
return this;
}

@CanIgnoreReturnValue
public JmxScraperContainer withService(String host, int port) {
// TODO: adding a way to provide 'host:port' syntax would make this easier for end users
this.serviceUrl =
String.format(
Locale.getDefault(), "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, port);
return this;
}

@CanIgnoreReturnValue
public JmxScraperContainer withCustomYaml(String yamlPath) {
this.customYaml.add(yamlPath);
return this;
}

@Override
public void start() {
// for now only configure through JVM args
List<String> arguments = new ArrayList<>();
arguments.add("java");
arguments.add("-Dotel.exporter.otlp.endpoint=" + endpoint);

if (!targetSystems.isEmpty()) {
arguments.add("-Dotel.jmx.target.system=" + String.join(",", targetSystems));
}

if (serviceUrl == null) {
throw new IllegalStateException("Missing service URL");
}
arguments.add("-Dotel.jmx.service.url=" + serviceUrl);
arguments.add("-Dotel.jmx.interval.milliseconds=" + intervalMillis);

if (!customYaml.isEmpty()) {
int i = 0;
StringBuilder sb = new StringBuilder("-Dotel.jmx.config=");
for (String yaml : customYaml) {
String containerPath = "/custom_" + i + ".yaml";
this.withCopyFileToContainer(MountableFile.forClasspathResource(yaml), containerPath);
if (i > 0) {
sb.append(",");
}
sb.append(containerPath);
i++;
}
arguments.add(sb.toString());
}

arguments.add("-jar");
arguments.add("/scraper.jar");

this.withCommand(arguments.toArray(new String[0]));

logger().info("Starting scraper with command: " + String.join(" ", arguments));

super.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.jmxscraper;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.shaded.com.google.errorprone.annotations.CanIgnoreReturnValue;
import org.testcontainers.utility.MountableFile;

/** Test container that allows to execute {@link TestApp} in an isolated container */
public class TestAppContainer extends GenericContainer<TestAppContainer> {

private final Map<String, String> properties;
private int port;
private String login;
private String pwd;

public TestAppContainer() {
super("openjdk:8u272-jre-slim");

this.properties = new HashMap<>();

String appJar = System.getProperty("app.jar.path");
assertThat(Paths.get(appJar)).isNotEmptyFile().isReadable();

this.withCopyFileToContainer(MountableFile.forHostPath(appJar), "/app.jar")
.waitingFor(
Wait.forLogMessage(TestApp.APP_STARTED_MSG + "\\n", 1)
.withStartupTimeout(Duration.ofSeconds(5)))
.withCommand("java", "-jar", "/app.jar");
}

@CanIgnoreReturnValue
public TestAppContainer withJmxPort(int port) {
this.port = port;
properties.put("com.sun.management.jmxremote.port", Integer.toString(port));
return this.withExposedPorts(port);
}

@CanIgnoreReturnValue
public TestAppContainer withUserAuth(String login, String pwd) {
this.login = login;
this.pwd = pwd;
return this;
}

@Override
protected void doStart() {
super.doStart();
}

@Override
public void start() {

// TODO: add support for ssl
properties.put("com.sun.management.jmxremote.ssl", "false");

if (pwd == null) {
properties.put("com.sun.management.jmxremote.authenticate", "false");
} else {
properties.put("com.sun.management.jmxremote.authenticate", "true");

Path pwdFile = createPwdFile(login, pwd);
this.withCopyFileToContainer(MountableFile.forHostPath(pwdFile), "/jmx.password");
properties.put("com.sun.management.jmxremote.password.file", "/jmx.password");

Path accessFile = createAccessFile(login);
this.withCopyFileToContainer(MountableFile.forHostPath(accessFile), "/jmx.access");
properties.put("com.sun.management.jmxremote.access.file", "/jmx.access");
}

String confArgs =
properties.entrySet().stream()
.map(
e -> {
String s = "-D" + e.getKey();
if (!e.getValue().isEmpty()) {
s += "=" + e.getValue();
}
return s;
})
.collect(Collectors.joining(" "));

this.withEnv("JAVA_TOOL_OPTIONS", confArgs);

logger().info("Test application JAVA_TOOL_OPTIONS = " + confArgs);

super.start();

logger().info("Test application JMX port mapped to {}:{}", getHost(), getMappedPort(port));
}

private static Path createPwdFile(String login, String pwd) {
try {
Path path = Files.createTempFile("test", ".pwd");
writeLine(path, String.format("%s %s", login, pwd));
return path;
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static Path createAccessFile(String login) {
try {
Path path = Files.createTempFile("test", ".pwd");
writeLine(path, String.format("%s %s", login, "readwrite"));
return path;
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static void writeLine(Path path, String line) throws IOException {
line = line + "\n";
Files.write(path, line.getBytes(StandardCharsets.UTF_8));
}
}
Loading