Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bootstrap integration tests #4

Merged
merged 10 commits into from
Sep 19, 2024
Prev Previous commit
Next Next commit
refactor to use test containers
SylvainJuge committed Sep 18, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 91976c06da0b4a10452760284ee5a3a1ecdb805d
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.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;
SylvainJuge marked this conversation as resolved.
Show resolved Hide resolved

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;
}

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

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

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;
}

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";
SylvainJuge marked this conversation as resolved.
Show resolved Hide resolved
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]));

super.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 static final Logger logger = LoggerFactory.getLogger(TestAppContainer.class);

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;
}

public int getPort() {
return getMappedPort(port);
}

@Override
protected void doStart() {
SylvainJuge marked this conversation as resolved.
Show resolved Hide resolved
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