Skip to content

Commit

Permalink
Integration tests for JMX Scraper (#1480)
Browse files Browse the repository at this point in the history
Co-authored-by: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com>
  • Loading branch information
robsunday and SylvainJuge authored Oct 16, 2024
1 parent ec5b648 commit b0aae23
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,25 @@ static void afterAll() {

@Test
void noAuth() {
try (TestAppContainer app = new TestAppContainer().withNetwork(network).withJmxPort(9990)) {
int port = PortSelector.getAvailableRandomPort();
try (TestAppContainer app = new TestAppContainer().withHostAccessFixedJmxPort(port)) {
app.start();
testConnector(
() -> JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(9990)).build());
() -> JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(port)).build());
}
}

@Test
void loginPwdAuth() {
int port = PortSelector.getAvailableRandomPort();
String login = "user";
String pwd = "t0p!Secret";
try (TestAppContainer app =
new TestAppContainer().withNetwork(network).withJmxPort(9999).withUserAuth(login, pwd)) {
new TestAppContainer().withHostAccessFixedJmxPort(port).withUserAuth(login, pwd)) {
app.start();
testConnector(
() ->
JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(9999))
JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(port))
.userCredentials(login, pwd)
.build());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.jmxscraper;

import java.io.IOException;
import java.net.Socket;
import java.util.Random;

/** Class used for finding random free network port from range 1024-65535 */
public class PortSelector {
private static final Random random = new Random(System.currentTimeMillis());

private static final int MIN_PORT = 1024;
private static final int MAX_PORT = 65535;

private PortSelector() {}

/**
* @return random available TCP port
*/
public static synchronized int getAvailableRandomPort() {
int port;

do {
port = random.nextInt(MAX_PORT - MIN_PORT + 1) + MIN_PORT;
} while (!isPortAvailable(port));

return port;
}

private static boolean isPortAvailable(int port) {
// see https://stackoverflow.com/a/13826145 for the chosen implementation
try (Socket s = new Socket("localhost", port)) {
return false;
} catch (IOException e) {
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
public class TestAppContainer extends GenericContainer<TestAppContainer> {

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

Expand All @@ -44,11 +43,16 @@ public TestAppContainer() {
.withCommand("java", "-jar", "/app.jar");
}

/**
* Configures app container for container-to-container access
*
* @param port mapped port to use
* @return this
*/
@CanIgnoreReturnValue
public TestAppContainer withJmxPort(int port) {
this.port = port;
properties.put("com.sun.management.jmxremote.port", Integer.toString(port));
return this.withExposedPorts(port);
return this;
}

@CanIgnoreReturnValue
Expand All @@ -58,9 +62,30 @@ public TestAppContainer withUserAuth(String login, String pwd) {
return this;
}

/**
* Configures app container for host-to-container access, port will be used as-is from host to
* work-around JMX in docker. This is optional on Linux as there is a network route and the
* container is accessible, but not on Mac where the container runs in an isolated VM.
*
* @param port port to use, must be available on host.
* @return this
*/
@CanIgnoreReturnValue
public TestAppContainer withHostAccessFixedJmxPort(int port) {
// To get host->container JMX connection working docker must expose JMX/RMI port under the same
// port number. Because of this testcontainers' standard exposed port randomization approach
// can't be used.
// Explanation:
// https://forums.docker.com/t/exposing-mapped-jmx-ports-from-multiple-containers/5287/6
properties.put("com.sun.management.jmxremote.port", Integer.toString(port));
properties.put("com.sun.management.jmxremote.rmi.port", Integer.toString(port));
properties.put("java.rmi.server.hostname", getHost());
addFixedExposedPort(port, port);
return this;
}

@Override
public void start() {

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

Expand Down Expand Up @@ -92,11 +117,9 @@ public void start() {

this.withEnv("JAVA_TOOL_OPTIONS", confArgs);

logger().info("Test application 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,19 @@

package io.opentelemetry.contrib.jmxscraper.target_systems;

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

import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.grpc.GrpcService;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
import io.grpc.stub.StreamObserver;
import io.opentelemetry.contrib.jmxscraper.JmxConnectorBuilder;
import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingDeque;
import javax.management.remote.JMXConnector;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
Expand All @@ -35,8 +30,8 @@
import org.testcontainers.containers.output.Slf4jLogConsumer;

public abstract class TargetSystemIntegrationTest {

private static final Logger logger = LoggerFactory.getLogger(TargetSystemIntegrationTest.class);
private static final Logger targetSystemLogger = LoggerFactory.getLogger("TargetSystemContainer");
private static final Logger jmxScraperLogger = LoggerFactory.getLogger("JmxScraperContainer");
private static final String TARGET_SYSTEM_NETWORK_ALIAS = "targetsystem";
private static String otlpEndpoint;

Expand All @@ -54,6 +49,9 @@ public abstract class TargetSystemIntegrationTest {
private JmxScraperContainer scraper;

private static final String OTLP_HOST = "host.testcontainers.internal";

// JMX communication only happens between container, and we don't have to use JMX
// from host to container, we can use a fixed port.
private static final int JMX_PORT = 9999;

@BeforeAll
Expand Down Expand Up @@ -93,27 +91,14 @@ void endToEndTest() {

target =
createTargetContainer(JMX_PORT)
.withLogConsumer(new Slf4jLogConsumer(logger))
.withLogConsumer(new Slf4jLogConsumer(targetSystemLogger))
.withNetwork(network)
.withExposedPorts(JMX_PORT)
.withNetworkAliases(TARGET_SYSTEM_NETWORK_ALIAS);
target.start();

String targetHost = target.getHost();
Integer targetPort = target.getMappedPort(JMX_PORT);
logger.info(
"Target system started, JMX port: {} mapped to {}:{}", JMX_PORT, targetHost, targetPort);

// TODO : wait for metrics to be sent and add assertions on what is being captured
// for now we just test that we can connect to remote JMX using our client.
try (JMXConnector connector = JmxConnectorBuilder.createNew(targetHost, targetPort).build()) {
assertThat(connector.getMBeanServerConnection()).isNotNull();
} catch (IOException e) {
throw new RuntimeException(e);
}

scraper =
new JmxScraperContainer(otlpEndpoint)
.withLogConsumer(new Slf4jLogConsumer(jmxScraperLogger))
.withNetwork(network)
.withService(TARGET_SYSTEM_NETWORK_ALIAS, JMX_PORT);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ private Map<String, Object> buildEnv() {
@SuppressWarnings("BanJNDI")
private static JMXConnector doConnect(JMXServiceURL url, Map<String, Object> env)
throws IOException {
logger.info("Connecting to " + url);
return JMXConnectorFactory.connect(url, env);
}

Expand Down

0 comments on commit b0aae23

Please sign in to comment.