Skip to content

Commit b0aae23

Browse files
Integration tests for JMX Scraper (#1480)
Co-authored-by: Sylvain Juge <[email protected]>
1 parent ec5b648 commit b0aae23

File tree

5 files changed

+86
-33
lines changed

5 files changed

+86
-33
lines changed

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilderTest.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,25 @@ static void afterAll() {
3131

3232
@Test
3333
void noAuth() {
34-
try (TestAppContainer app = new TestAppContainer().withNetwork(network).withJmxPort(9990)) {
34+
int port = PortSelector.getAvailableRandomPort();
35+
try (TestAppContainer app = new TestAppContainer().withHostAccessFixedJmxPort(port)) {
3536
app.start();
3637
testConnector(
37-
() -> JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(9990)).build());
38+
() -> JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(port)).build());
3839
}
3940
}
4041

4142
@Test
4243
void loginPwdAuth() {
44+
int port = PortSelector.getAvailableRandomPort();
4345
String login = "user";
4446
String pwd = "t0p!Secret";
4547
try (TestAppContainer app =
46-
new TestAppContainer().withNetwork(network).withJmxPort(9999).withUserAuth(login, pwd)) {
48+
new TestAppContainer().withHostAccessFixedJmxPort(port).withUserAuth(login, pwd)) {
4749
app.start();
4850
testConnector(
4951
() ->
50-
JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(9999))
52+
JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(port))
5153
.userCredentials(login, pwd)
5254
.build());
5355
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.jmxscraper;
7+
8+
import java.io.IOException;
9+
import java.net.Socket;
10+
import java.util.Random;
11+
12+
/** Class used for finding random free network port from range 1024-65535 */
13+
public class PortSelector {
14+
private static final Random random = new Random(System.currentTimeMillis());
15+
16+
private static final int MIN_PORT = 1024;
17+
private static final int MAX_PORT = 65535;
18+
19+
private PortSelector() {}
20+
21+
/**
22+
* @return random available TCP port
23+
*/
24+
public static synchronized int getAvailableRandomPort() {
25+
int port;
26+
27+
do {
28+
port = random.nextInt(MAX_PORT - MIN_PORT + 1) + MIN_PORT;
29+
} while (!isPortAvailable(port));
30+
31+
return port;
32+
}
33+
34+
private static boolean isPortAvailable(int port) {
35+
// see https://stackoverflow.com/a/13826145 for the chosen implementation
36+
try (Socket s = new Socket("localhost", port)) {
37+
return false;
38+
} catch (IOException e) {
39+
return true;
40+
}
41+
}
42+
}

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppContainer.java

+30-7
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
public class TestAppContainer extends GenericContainer<TestAppContainer> {
2626

2727
private final Map<String, String> properties;
28-
private int port;
2928
private String login;
3029
private String pwd;
3130

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

46+
/**
47+
* Configures app container for container-to-container access
48+
*
49+
* @param port mapped port to use
50+
* @return this
51+
*/
4752
@CanIgnoreReturnValue
4853
public TestAppContainer withJmxPort(int port) {
49-
this.port = port;
5054
properties.put("com.sun.management.jmxremote.port", Integer.toString(port));
51-
return this.withExposedPorts(port);
55+
return this;
5256
}
5357

5458
@CanIgnoreReturnValue
@@ -58,9 +62,30 @@ public TestAppContainer withUserAuth(String login, String pwd) {
5862
return this;
5963
}
6064

65+
/**
66+
* Configures app container for host-to-container access, port will be used as-is from host to
67+
* work-around JMX in docker. This is optional on Linux as there is a network route and the
68+
* container is accessible, but not on Mac where the container runs in an isolated VM.
69+
*
70+
* @param port port to use, must be available on host.
71+
* @return this
72+
*/
73+
@CanIgnoreReturnValue
74+
public TestAppContainer withHostAccessFixedJmxPort(int port) {
75+
// To get host->container JMX connection working docker must expose JMX/RMI port under the same
76+
// port number. Because of this testcontainers' standard exposed port randomization approach
77+
// can't be used.
78+
// Explanation:
79+
// https://forums.docker.com/t/exposing-mapped-jmx-ports-from-multiple-containers/5287/6
80+
properties.put("com.sun.management.jmxremote.port", Integer.toString(port));
81+
properties.put("com.sun.management.jmxremote.rmi.port", Integer.toString(port));
82+
properties.put("java.rmi.server.hostname", getHost());
83+
addFixedExposedPort(port, port);
84+
return this;
85+
}
86+
6187
@Override
6288
public void start() {
63-
6489
// TODO: add support for ssl
6590
properties.put("com.sun.management.jmxremote.ssl", "false");
6691

@@ -92,11 +117,9 @@ public void start() {
92117

93118
this.withEnv("JAVA_TOOL_OPTIONS", confArgs);
94119

95-
logger().info("Test application JAVA_TOOL_OPTIONS = " + confArgs);
120+
logger().info("Test application JAVA_TOOL_OPTIONS = {}", confArgs);
96121

97122
super.start();
98-
99-
logger().info("Test application JMX port mapped to {}:{}", getHost(), getMappedPort(port));
100123
}
101124

102125
private static Path createPwdFile(String login, String pwd) {

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TargetSystemIntegrationTest.java

+7-22
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,19 @@
55

66
package io.opentelemetry.contrib.jmxscraper.target_systems;
77

8-
import static org.assertj.core.api.Assertions.assertThat;
9-
108
import com.linecorp.armeria.server.ServerBuilder;
119
import com.linecorp.armeria.server.grpc.GrpcService;
1210
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
1311
import io.grpc.stub.StreamObserver;
14-
import io.opentelemetry.contrib.jmxscraper.JmxConnectorBuilder;
1512
import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer;
1613
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
1714
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
1815
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
19-
import java.io.IOException;
2016
import java.util.ArrayList;
2117
import java.util.List;
2218
import java.util.concurrent.BlockingQueue;
2319
import java.util.concurrent.ExecutionException;
2420
import java.util.concurrent.LinkedBlockingDeque;
25-
import javax.management.remote.JMXConnector;
2621
import org.junit.jupiter.api.AfterAll;
2722
import org.junit.jupiter.api.AfterEach;
2823
import org.junit.jupiter.api.BeforeAll;
@@ -35,8 +30,8 @@
3530
import org.testcontainers.containers.output.Slf4jLogConsumer;
3631

3732
public abstract class TargetSystemIntegrationTest {
38-
39-
private static final Logger logger = LoggerFactory.getLogger(TargetSystemIntegrationTest.class);
33+
private static final Logger targetSystemLogger = LoggerFactory.getLogger("TargetSystemContainer");
34+
private static final Logger jmxScraperLogger = LoggerFactory.getLogger("JmxScraperContainer");
4035
private static final String TARGET_SYSTEM_NETWORK_ALIAS = "targetsystem";
4136
private static String otlpEndpoint;
4237

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

5651
private static final String OTLP_HOST = "host.testcontainers.internal";
52+
53+
// JMX communication only happens between container, and we don't have to use JMX
54+
// from host to container, we can use a fixed port.
5755
private static final int JMX_PORT = 9999;
5856

5957
@BeforeAll
@@ -93,27 +91,14 @@ void endToEndTest() {
9391

9492
target =
9593
createTargetContainer(JMX_PORT)
96-
.withLogConsumer(new Slf4jLogConsumer(logger))
94+
.withLogConsumer(new Slf4jLogConsumer(targetSystemLogger))
9795
.withNetwork(network)
98-
.withExposedPorts(JMX_PORT)
9996
.withNetworkAliases(TARGET_SYSTEM_NETWORK_ALIAS);
10097
target.start();
10198

102-
String targetHost = target.getHost();
103-
Integer targetPort = target.getMappedPort(JMX_PORT);
104-
logger.info(
105-
"Target system started, JMX port: {} mapped to {}:{}", JMX_PORT, targetHost, targetPort);
106-
107-
// TODO : wait for metrics to be sent and add assertions on what is being captured
108-
// for now we just test that we can connect to remote JMX using our client.
109-
try (JMXConnector connector = JmxConnectorBuilder.createNew(targetHost, targetPort).build()) {
110-
assertThat(connector.getMBeanServerConnection()).isNotNull();
111-
} catch (IOException e) {
112-
throw new RuntimeException(e);
113-
}
114-
11599
scraper =
116100
new JmxScraperContainer(otlpEndpoint)
101+
.withLogConsumer(new Slf4jLogConsumer(jmxScraperLogger))
117102
.withNetwork(network)
118103
.withService(TARGET_SYSTEM_NETWORK_ALIAS, JMX_PORT);
119104

jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilder.java

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ private Map<String, Object> buildEnv() {
138138
@SuppressWarnings("BanJNDI")
139139
private static JMXConnector doConnect(JMXServiceURL url, Map<String, Object> env)
140140
throws IOException {
141+
logger.info("Connecting to " + url);
141142
return JMXConnectorFactory.connect(url, env);
142143
}
143144

0 commit comments

Comments
 (0)