Skip to content

Commit 6d1931b

Browse files
authored
Merge branch 'main' into renovate/otelinstrumentationversion
2 parents 4d93ca3 + 4ce7187 commit 6d1931b

File tree

10 files changed

+531
-70
lines changed

10 files changed

+531
-70
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ feature or via instrumentation, this project is hopefully for you.
1515
## Provided Libraries
1616

1717
| Status* | Library |
18-
| ------- |-------------------------------------------------------------------|
18+
|---------|-------------------------------------------------------------------|
1919
| beta | [AWS Resources](./aws-resources/README.md) |
2020
| stable | [AWS X-Ray SDK Support](./aws-xray/README.md) |
2121
| alpha | [AWS X-Ray Propagator](./aws-xray-propagator/README.md) |
22-
| alpha | [Baggage Processors](./baggage-processor/README.md) |
22+
| alpha | [Baggage Processors](./baggage-processor/README.md) |
2323
| alpha | [zstd Compressor](./compressors/compressor-zstd/README.md) |
2424
| alpha | [Consistent Sampling](./consistent-sampling/README.md) |
2525
| alpha | [Disk Buffering](./disk-buffering/README.md) |
@@ -29,6 +29,7 @@ feature or via instrumentation, this project is hopefully for you.
2929
| alpha | [JFR Connection](./jfr-connection/README.md) |
3030
| alpha | [JFR Events](./jfr-events/README.md) |
3131
| alpha | [JMX Metric Gatherer](./jmx-metrics/README.md) |
32+
| alpha | [JMX Metric Scraper](./jmx-scraper/README.md) |
3233
| alpha | [Kafka Support](./kafka-exporter/README.md) |
3334
| alpha | [OpenTelemetry Maven Extension](./maven-extension/README.md) |
3435
| alpha | [Micrometer MeterProvider](./micrometer-meter-provider/README.md) |

jmx-scraper/README.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,29 @@ Configuration can be provided through:
2929
`otel.jmx.service.url=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi` is written to stdin.
3030
- environment variables: `OTEL_JMX_TARGET_SYSTEM=tomcat OTEL_JMX_SERVICE_URL=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi java -jar scraper.jar`
3131

32-
SDK auto-configuration is being used, so all the configuration options can be set using the java
32+
SDK autoconfiguration is being used, so all the configuration options can be set using the java
3333
properties syntax or the corresponding environment variables.
3434

3535
For example the `otel.jmx.service.url` option can be set with the `OTEL_JMX_SERVICE_URL` environment variable.
3636

3737
## Configuration reference
3838

39-
| config option | description |
40-
|--------------------------|---------------------------------------------------------------------------------------------------------------------|
41-
| `otel.jmx.service.url` | mandatory JMX URL to connect to the remote JVM |
42-
| `otel.jmx.target.system` | comma-separated list of systems to monitor, mandatory unless a custom configuration is used |
43-
| `otel.jmx.config` | comma-separated list of paths to custom YAML metrics definition, mandatory when `otel.jmx.target.system` is not set |
44-
| `otel.jmx.username` | user name for JMX connection, mandatory when JMX authentication is enabled on target JVM |
45-
| `otel.jmx.password` | password for JMX connection, mandatory when JMX authentication is enabled on target JVM |
39+
| config option | default value | description |
40+
|--------------------------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------|
41+
| `otel.jmx.service.url` | - | mandatory JMX URL to connect to the remote JVM |
42+
| `otel.jmx.target.system` | - | comma-separated list of systems to monitor, mandatory unless `otel.jmx.config` is set |
43+
| `otel.jmx.config` | empty | comma-separated list of paths to custom YAML metrics definition, mandatory when `otel.jmx.target.system` is not set |
44+
| `otel.jmx.username` | - | user name for JMX connection, mandatory when JMX authentication is set on target JVM with`com.sun.management.jmxremote.authenticate=true` |
45+
| `otel.jmx.password` | - | password for JMX connection, mandatory when JMX authentication is set on target JVM with `com.sun.management.jmxremote.authenticate=true` |
46+
| `otel.jmx.remote.registry.ssl` | `false` | connect to an SSL-protected registry when enabled on target JVM with `com.sun.management.jmxremote.registry.ssl=true` |
47+
48+
When both `otel.jmx.target.system` and `otel.jmx.config` configuration options are used at the same time:
49+
50+
- `otel.jmx.target.system` provides ready-to-use metrics and `otel.jmx.config` allows to add custom definitions.
51+
- The metrics definitions will be the aggregation of both.
52+
- There is no guarantee on the priority or any ability to override the definitions.
53+
54+
If there is a need to override existing ready-to-use metrics or to keep control on the metrics definitions, using a custom YAML definition with `otel.jmx.config` is the recommended option.
4655

4756
Supported values for `otel.jmx.target.system`:
4857

jmx-scraper/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ testing {
3737
implementation("com.linecorp.armeria:armeria-junit5")
3838
implementation("com.linecorp.armeria:armeria-grpc")
3939
implementation("io.opentelemetry.proto:opentelemetry-proto:1.5.0-alpha")
40+
implementation("org.bouncycastle:bcprov-jdk18on:1.80")
41+
implementation("org.bouncycastle:bcpkix-jdk18on:1.80")
4042
}
4143
}
4244
}

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

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.junit.jupiter.api.AfterAll;
1313
import org.junit.jupiter.api.BeforeAll;
1414
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.io.TempDir;
1516
import org.slf4j.Logger;
1617
import org.slf4j.LoggerFactory;
1718
import org.testcontainers.containers.GenericContainer;
@@ -32,6 +33,10 @@ public class JmxConnectionTest {
3233
private static final int JMX_PORT = 9999;
3334
private static final String APP_HOST = "app";
3435

36+
// key/trust stores passwords
37+
private static final String CLIENT_PASSWORD = "client";
38+
private static final String SERVER_PASSWORD = "server";
39+
3540
private static final Logger jmxScraperLogger = LoggerFactory.getLogger("JmxScraperContainer");
3641
private static final Logger appLogger = LoggerFactory.getLogger("TestAppContainer");
3742

@@ -71,6 +76,84 @@ void userPassword() {
7176
scraper -> scraper.withRmiServiceUrl(APP_HOST, JMX_PORT).withUser(login).withPassword(pwd));
7277
}
7378

79+
@Test
80+
void serverSsl(@TempDir Path tempDir) {
81+
testServerSsl(tempDir, /* sslRmiRegistry= */ false);
82+
}
83+
84+
@Test
85+
void serverSslWithSslRmiRegistry(@TempDir Path tempDir) {
86+
testServerSsl(tempDir, /* sslRmiRegistry= */ true);
87+
}
88+
89+
private static void testServerSsl(Path tempDir, boolean sslRmiRegistry) {
90+
// two keystores:
91+
// server keystore with public/private key pair
92+
// client trust store with certificate from server
93+
94+
TestKeyStore serverKeyStore =
95+
TestKeyStore.newKeyStore(tempDir.resolve("server.jks"), SERVER_PASSWORD);
96+
TestKeyStore clientTrustStore =
97+
TestKeyStore.newKeyStore(tempDir.resolve("client.jks"), CLIENT_PASSWORD);
98+
99+
X509Certificate serverCertificate = serverKeyStore.addKeyPair();
100+
clientTrustStore.addTrustedCertificate(serverCertificate);
101+
102+
connectionTest(
103+
app ->
104+
(sslRmiRegistry ? app.withSslRmiRegistry(4242) : app)
105+
.withJmxPort(JMX_PORT)
106+
.withJmxSsl()
107+
.withKeyStore(serverKeyStore),
108+
scraper ->
109+
(sslRmiRegistry ? scraper.withSslRmiRegistry() : scraper)
110+
.withRmiServiceUrl(APP_HOST, JMX_PORT)
111+
.withTrustStore(clientTrustStore));
112+
}
113+
114+
@Test
115+
void serverSslClientSsl(@TempDir Path tempDir) {
116+
// Note: this could have been made simpler by relying on the fact that keystore could be used
117+
// as a trust store, but having clear split provides also some extra clarity
118+
//
119+
// 4 keystores:
120+
// server keystore with public/private key pair
121+
// server truststore with client certificate
122+
// client key store with public/private key pair
123+
// client trust store with certificate from server
124+
125+
TestKeyStore serverKeyStore =
126+
TestKeyStore.newKeyStore(tempDir.resolve("server-keystore.jks"), SERVER_PASSWORD);
127+
TestKeyStore serverTrustStore =
128+
TestKeyStore.newKeyStore(tempDir.resolve("server-truststore.jks"), SERVER_PASSWORD);
129+
130+
X509Certificate serverCertificate = serverKeyStore.addKeyPair();
131+
132+
TestKeyStore clientKeyStore =
133+
TestKeyStore.newKeyStore(tempDir.resolve("client-keystore.jks"), CLIENT_PASSWORD);
134+
TestKeyStore clientTrustStore =
135+
TestKeyStore.newKeyStore(tempDir.resolve("client-truststore.jks"), CLIENT_PASSWORD);
136+
137+
X509Certificate clientCertificate = clientKeyStore.addKeyPair();
138+
139+
// adding certificates in trust stores
140+
clientTrustStore.addTrustedCertificate(serverCertificate);
141+
serverTrustStore.addTrustedCertificate(clientCertificate);
142+
143+
connectionTest(
144+
app ->
145+
app.withJmxPort(JMX_PORT)
146+
.withJmxSsl()
147+
.withClientSslCertificate()
148+
.withKeyStore(serverKeyStore)
149+
.withTrustStore(serverTrustStore),
150+
scraper ->
151+
scraper
152+
.withRmiServiceUrl(APP_HOST, JMX_PORT)
153+
.withKeyStore(clientKeyStore)
154+
.withTrustStore(clientTrustStore));
155+
}
156+
74157
private static void connectionTest(
75158
Function<TestAppContainer, TestAppContainer> customizeApp,
76159
Function<JmxScraperContainer, JmxScraperContainer> customizeScraper) {
@@ -87,17 +170,23 @@ private static void connectionTest(
87170
private static void checkConnectionLogs(JmxScraperContainer scraper, boolean expectedOk) {
88171

89172
String[] logLines = scraper.getLogs().split("\n");
90-
String lastLine = logLines[logLines.length - 1];
91-
92-
if (expectedOk) {
93-
assertThat(lastLine)
94-
.describedAs("should log connection success")
95-
.endsWith("JMX connection test OK");
96-
} else {
97-
assertThat(lastLine)
98-
.describedAs("should log connection failure")
99-
.endsWith("JMX connection test ERROR");
100-
}
173+
174+
// usually only the last line can be checked, however when it fails with an exception
175+
// the stack trace is last in the output, so it's simpler to check all lines of log output
176+
177+
assertThat(logLines)
178+
.anySatisfy(
179+
line -> {
180+
if (expectedOk) {
181+
assertThat(line)
182+
.describedAs("should log connection success")
183+
.contains("JMX connection test OK");
184+
} else {
185+
assertThat(line)
186+
.describedAs("should log connection failure")
187+
.contains("JMX connection test ERROR");
188+
}
189+
});
101190
}
102191

103192
private static void waitTerminated(GenericContainer<?> container) {

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

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
import static org.assertj.core.api.Assertions.assertThat;
99

1010
import com.google.errorprone.annotations.CanIgnoreReturnValue;
11+
import java.nio.file.Path;
1112
import java.time.Duration;
1213
import java.util.ArrayList;
14+
import java.util.Arrays;
15+
import java.util.Collections;
1316
import java.util.HashSet;
1417
import java.util.List;
1518
import java.util.Locale;
@@ -29,6 +32,9 @@ public class JmxScraperContainer extends GenericContainer<JmxScraperContainer> {
2932
private String password;
3033
private final List<String> extraJars;
3134
private boolean testJmx;
35+
private TestKeyStore keyStore;
36+
private TestKeyStore trustStore;
37+
private boolean sslRmiRegistry;
3238

3339
public JmxScraperContainer(String otlpEndpoint, String baseImage) {
3440
super(baseImage);
@@ -44,20 +50,38 @@ public JmxScraperContainer(String otlpEndpoint, String baseImage) {
4450
this.extraJars = new ArrayList<>();
4551
}
4652

53+
/**
54+
* Adds a target system
55+
*
56+
* @param targetSystem target system
57+
* @return this
58+
*/
4759
@CanIgnoreReturnValue
4860
public JmxScraperContainer withTargetSystem(String targetSystem) {
4961
targetSystems.add(targetSystem);
5062
return this;
5163
}
5264

65+
/**
66+
* Set connection to a standard JMX service URL
67+
*
68+
* @param host JMX host
69+
* @param port JMX port
70+
* @return this
71+
*/
5372
@CanIgnoreReturnValue
5473
public JmxScraperContainer withRmiServiceUrl(String host, int port) {
55-
// TODO: adding a way to provide 'host:port' syntax would make this easier for end users
5674
return withServiceUrl(
5775
String.format(
5876
Locale.getDefault(), "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, port));
5977
}
6078

79+
/**
80+
* Set connection to a JMX service URL
81+
*
82+
* @param serviceUrl service URL
83+
* @return this
84+
*/
6185
@CanIgnoreReturnValue
6286
public JmxScraperContainer withServiceUrl(String serviceUrl) {
6387
this.serviceUrl = serviceUrl;
@@ -112,12 +136,52 @@ public JmxScraperContainer withCustomYaml(String yamlPath) {
112136
return this;
113137
}
114138

139+
/**
140+
* Configure the scraper JVM to only test connection with the JMX endpoint
141+
*
142+
* @return this
143+
*/
115144
@CanIgnoreReturnValue
116145
public JmxScraperContainer withTestJmx() {
117146
this.testJmx = true;
118147
return this;
119148
}
120149

150+
/**
151+
* Configure key store for the scraper JVM
152+
*
153+
* @param keyStore key store
154+
* @return this
155+
*/
156+
@CanIgnoreReturnValue
157+
public JmxScraperContainer withKeyStore(TestKeyStore keyStore) {
158+
this.keyStore = keyStore;
159+
return this;
160+
}
161+
162+
/**
163+
* Configure trust store for the scraper JVM
164+
*
165+
* @param trustStore trust store
166+
* @return this
167+
*/
168+
@CanIgnoreReturnValue
169+
public JmxScraperContainer withTrustStore(TestKeyStore trustStore) {
170+
this.trustStore = trustStore;
171+
return this;
172+
}
173+
174+
/**
175+
* Enables connection to an SSL-protected RMI registry
176+
*
177+
* @return this
178+
*/
179+
@CanIgnoreReturnValue
180+
public JmxScraperContainer withSslRmiRegistry() {
181+
this.sslRmiRegistry = true;
182+
return this;
183+
}
184+
121185
@Override
122186
public void start() {
123187
// for now only configure through JVM args
@@ -144,6 +208,13 @@ public void start() {
144208
arguments.add("-Dotel.jmx.password=" + password);
145209
}
146210

211+
arguments.addAll(addSecureStore(keyStore, /* isKeyStore= */ true));
212+
arguments.addAll(addSecureStore(trustStore, /* isKeyStore= */ false));
213+
214+
if (sslRmiRegistry) {
215+
arguments.add("-Dotel.jmx.remote.registry.ssl=true");
216+
}
217+
147218
if (!customYamlFiles.isEmpty()) {
148219
for (String yaml : customYamlFiles) {
149220
this.withCopyFileToContainer(MountableFile.forClasspathResource(yaml), yaml);
@@ -177,4 +248,17 @@ public void start() {
177248

178249
super.start();
179250
}
251+
252+
private List<String> addSecureStore(TestKeyStore keyStore, boolean isKeyStore) {
253+
if (keyStore == null) {
254+
return Collections.emptyList();
255+
}
256+
Path path = keyStore.getPath();
257+
String containerPath = "/" + path.getFileName().toString();
258+
this.withCopyFileToContainer(MountableFile.forHostPath(path), containerPath);
259+
260+
String prefix = String.format("-Djavax.net.ssl.%sStore", isKeyStore ? "key" : "trust");
261+
return Arrays.asList(
262+
prefix + "=" + containerPath, prefix + "Password=" + keyStore.getPassword());
263+
}
180264
}

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

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)