Skip to content

Commit 0b77afa

Browse files
committed
Add Kafka contract tests.
1 parent 14cf12a commit 0b77afa

File tree

13 files changed

+748
-330
lines changed

13 files changed

+748
-330
lines changed

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/base/ContractTestBase.java

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ public abstract class ContractTestBase {
4848

4949
private final Logger collectorLogger =
5050
LoggerFactory.getLogger("collector " + getApplicationOtelServiceName());
51-
private final Logger applicationLogger =
51+
protected final Logger applicationLogger =
5252
LoggerFactory.getLogger("application " + getApplicationOtelServiceName());
5353

54-
private static final String AGENT_PATH =
54+
protected static final String AGENT_PATH =
5555
System.getProperty("io.awsobservability.instrumentation.contracttests.agentPath");
5656

5757
protected final Network network = Network.newNetwork();
5858

59-
private static final String COLLECTOR_HOSTNAME = "collector";
60-
private static final int COLLECTOR_PORT = 4317;
59+
protected static final String COLLECTOR_HOSTNAME = "collector";
60+
protected static final int COLLECTOR_PORT = 4317;
6161

6262
protected final GenericContainer<?> mockCollector =
6363
new GenericContainer<>("aws-appsignals-mock-collector")
@@ -67,30 +67,7 @@ public abstract class ContractTestBase {
6767
.withNetwork(network)
6868
.withNetworkAliases(COLLECTOR_HOSTNAME);
6969

70-
protected final GenericContainer<?> application =
71-
new GenericContainer<>(getApplicationImageName())
72-
.dependsOn(getDependsOn())
73-
.withExposedPorts(getApplicationPort())
74-
.withNetwork(network)
75-
.withLogConsumer(new Slf4jLogConsumer(applicationLogger))
76-
.withCopyFileToContainer(
77-
MountableFile.forHostPath(AGENT_PATH), "/opentelemetry-javaagent-all.jar")
78-
.waitingFor(getApplicationWaitCondition())
79-
.withEnv("JAVA_TOOL_OPTIONS", "-javaagent:/opentelemetry-javaagent-all.jar")
80-
.withEnv("OTEL_METRIC_EXPORT_INTERVAL", "100") // 100 ms
81-
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "true")
82-
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED", isRuntimeEnabled())
83-
.withEnv("OTEL_METRICS_EXPORTER", "none")
84-
.withEnv("OTEL_BSP_SCHEDULE_DELAY", "0") // Don't wait to export spans to the collector
85-
.withEnv(
86-
"OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT",
87-
"http://" + COLLECTOR_HOSTNAME + ":" + COLLECTOR_PORT)
88-
.withEnv(
89-
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
90-
"http://" + COLLECTOR_HOSTNAME + ":" + COLLECTOR_PORT)
91-
.withEnv("OTEL_RESOURCE_ATTRIBUTES", getApplicationOtelResourceAttributes())
92-
.withEnv(getApplicationExtraEnvironmentVariables())
93-
.withNetworkAliases(getApplicationNetworkAliases().toArray(new String[0]));
70+
protected final GenericContainer<?> application = getApplicationContainer();
9471

9572
protected MockCollectorClient mockCollectorClient;
9673
protected WebClient appClient;
@@ -109,10 +86,8 @@ private void stopCollector() {
10986
protected void setupClients() {
11087
application.start();
11188

112-
appClient = WebClient.of("http://localhost:" + application.getMappedPort(8080));
113-
mockCollectorClient =
114-
new MockCollectorClient(
115-
WebClient.of("http://localhost:" + mockCollector.getMappedPort(4317)));
89+
appClient = getApplicationClient();
90+
mockCollectorClient = getMockCollectorClient();
11691
}
11792

11893
@AfterEach
@@ -128,6 +103,41 @@ private List<Startable> getDependsOn() {
128103
return dependencies;
129104
}
130105

106+
protected WebClient getApplicationClient() {
107+
return WebClient.of("http://localhost:" + application.getMappedPort(8080));
108+
}
109+
110+
protected MockCollectorClient getMockCollectorClient() {
111+
return new MockCollectorClient(
112+
WebClient.of("http://localhost:" + mockCollector.getMappedPort(4317)));
113+
}
114+
115+
protected GenericContainer<?> getApplicationContainer() {
116+
return new GenericContainer<>(getApplicationImageName())
117+
.dependsOn(getDependsOn())
118+
.withExposedPorts(getApplicationPort())
119+
.withNetwork(network)
120+
.withLogConsumer(new Slf4jLogConsumer(applicationLogger))
121+
.withCopyFileToContainer(
122+
MountableFile.forHostPath(AGENT_PATH), "/opentelemetry-javaagent-all.jar")
123+
.waitingFor(getApplicationWaitCondition())
124+
.withEnv("JAVA_TOOL_OPTIONS", "-javaagent:/opentelemetry-javaagent-all.jar")
125+
.withEnv("OTEL_METRIC_EXPORT_INTERVAL", "100") // 100 ms
126+
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "true")
127+
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED", isRuntimeEnabled())
128+
.withEnv("OTEL_METRICS_EXPORTER", "none")
129+
.withEnv("OTEL_BSP_SCHEDULE_DELAY", "0") // Don't wait to export spans to the collector
130+
.withEnv(
131+
"OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT",
132+
"http://" + COLLECTOR_HOSTNAME + ":" + COLLECTOR_PORT)
133+
.withEnv(
134+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
135+
"http://" + COLLECTOR_HOSTNAME + ":" + COLLECTOR_PORT)
136+
.withEnv("OTEL_RESOURCE_ATTRIBUTES", getApplicationOtelResourceAttributes())
137+
.withEnv(getApplicationExtraEnvironmentVariables())
138+
.withNetworkAliases(getApplicationNetworkAliases().toArray(new String[0]));
139+
}
140+
131141
/** Methods that should be overridden in sub classes * */
132142
protected int getApplicationPort() {
133143
return 8080;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.appsignals.test.base;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import com.google.common.collect.ImmutableSet;
21+
import io.opentelemetry.proto.metrics.v1.NumberDataPoint;
22+
import java.util.List;
23+
import java.util.Set;
24+
import java.util.function.Consumer;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
import software.amazon.opentelemetry.appsignals.test.utils.AppSignalsConstants;
28+
import software.amazon.opentelemetry.appsignals.test.utils.JMXMetricsConstants;
29+
30+
public abstract class RuntimeMetricsContractTestBase extends ContractTestBase {
31+
private static final Logger logger =
32+
LoggerFactory.getLogger(RuntimeMetricsContractTestBase.class);
33+
34+
@Override
35+
protected String isRuntimeEnabled() {
36+
return "true";
37+
}
38+
39+
protected void doTestRuntimeMetrics() {
40+
var response = appClient.get("/success").aggregate().join();
41+
42+
assertThat(response.status().isSuccess()).isTrue();
43+
assertRuntimeMetrics();
44+
}
45+
46+
protected void assertRuntimeMetrics() {
47+
var metrics = mockCollectorClient.getRuntimeMetrics(getExpectedMetrics());
48+
metrics.forEach(
49+
metric -> {
50+
var dataPoints = metric.getMetric().getGauge().getDataPointsList();
51+
// logger.info("checking {}: {}", metric.getMetric().getName(), dataPoints.size());
52+
assertGreaterThanOrEqual(dataPoints, getThreshold(metric.getMetric().getName()));
53+
});
54+
}
55+
56+
protected Set<String> getExpectedMetrics() {
57+
return ImmutableSet.<String>builder()
58+
// .addAll(JMXMetricsConstants.JVM_METRICS_SET)
59+
.addAll(AppSignalsConstants.SLO_METRICS_SET)
60+
.build();
61+
}
62+
63+
protected long getThreshold(String metricName) {
64+
long threshold = 0;
65+
switch (metricName) {
66+
// If maximum memory size is undefined, then value is -1
67+
// https://docs.oracle.com/en/java/javase/17/docs/api/java.management/java/lang/management/MemoryUsage.html#getMax()
68+
case JMXMetricsConstants.JVM_HEAP_MAX:
69+
case JMXMetricsConstants.JVM_NON_HEAP_MAX:
70+
case JMXMetricsConstants.JVM_POOL_MAX:
71+
threshold = -1;
72+
default:
73+
}
74+
return threshold;
75+
}
76+
77+
private void assertGreaterThanOrEqual(List<NumberDataPoint> dps, long threshold) {
78+
assertDataPoints(dps, (value) -> assertThat(value).isGreaterThanOrEqualTo(threshold));
79+
}
80+
81+
private void assertDataPoints(List<NumberDataPoint> dps, Consumer<Long> consumer) {
82+
dps.forEach(
83+
datapoint -> {
84+
// logger.info("datapoint value: {}", datapoint.getAsInt());
85+
consumer.accept(datapoint.getAsInt());
86+
});
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.appsignals.test.misc;
17+
18+
import com.google.common.collect.ImmutableSet;
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.TestInstance;
21+
import org.testcontainers.junit.jupiter.Testcontainers;
22+
import software.amazon.opentelemetry.appsignals.test.base.RuntimeMetricsContractTestBase;
23+
import software.amazon.opentelemetry.appsignals.test.utils.JMXMetricsConstants;
24+
25+
import java.util.Map;
26+
import java.util.Set;
27+
28+
/**
29+
* Tests in this class validate that the SDK will emit JVM metrics when Application Signals runtime
30+
* metrics are enabled.
31+
*/
32+
@Testcontainers(disabledWithoutDocker = true)
33+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
34+
public class RuntimeJvmMetricsTest extends RuntimeMetricsContractTestBase {
35+
@Test
36+
void testJvmMetrics() {
37+
doTestRuntimeMetrics();
38+
}
39+
40+
@Override
41+
protected String getApplicationImageName() {
42+
return "aws-appsignals-tests-http-server-spring-mvc";
43+
}
44+
45+
@Override
46+
protected String getApplicationWaitPattern() {
47+
return ".*Started Application.*";
48+
}
49+
50+
@Override
51+
protected Set<String> getExpectedMetrics() {
52+
return ImmutableSet.<String>builder()
53+
.addAll(JMXMetricsConstants.JVM_METRICS_SET)
54+
.addAll(super.getExpectedMetrics())
55+
.build();
56+
}
57+
58+
@Override
59+
protected Map<String, String> getApplicationExtraEnvironmentVariables() {
60+
return Map.of(
61+
"OTEL_JMX_ENABLED", "true",
62+
"OTEL_JMX_TARGET_SYSTEM", "jvm");
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.appsignals.test.misc;
17+
18+
import com.google.common.collect.ImmutableSet;
19+
import java.io.IOException;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.Set;
23+
import org.junit.jupiter.api.AfterAll;
24+
import org.junit.jupiter.api.BeforeAll;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.TestInstance;
27+
import org.testcontainers.containers.KafkaContainer;
28+
import org.testcontainers.containers.wait.strategy.Wait;
29+
import org.testcontainers.images.PullPolicy;
30+
import org.testcontainers.junit.jupiter.Testcontainers;
31+
import org.testcontainers.lifecycle.Startable;
32+
import org.testcontainers.utility.DockerImageName;
33+
import software.amazon.opentelemetry.appsignals.test.base.RuntimeMetricsContractTestBase;
34+
import software.amazon.opentelemetry.appsignals.test.utils.JMXMetricsConstants;
35+
36+
/**
37+
* Tests in this class validate that the SDK will emit JVM metrics when Application Signals runtime
38+
* metrics are enabled.
39+
*/
40+
@Testcontainers(disabledWithoutDocker = true)
41+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
42+
public class RuntimeKafkaConsumerMetricsTest extends RuntimeMetricsContractTestBase {
43+
private KafkaContainer kafka;
44+
45+
@Test
46+
void testKafkaMetrics() {
47+
doTestRuntimeMetrics();
48+
}
49+
50+
@Override
51+
protected List<Startable> getApplicationDependsOnContainers() {
52+
kafka =
53+
new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.4.0"))
54+
.withImagePullPolicy(PullPolicy.alwaysPull())
55+
.withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "false")
56+
.withNetworkAliases("kafkaBroker")
57+
.withNetwork(network)
58+
.waitingFor(Wait.forLogMessage(".* Kafka Server started .*", 1))
59+
.withKraft();
60+
return List.of(kafka);
61+
}
62+
63+
@BeforeAll
64+
public void setup() throws IOException, InterruptedException {
65+
kafka.start();
66+
kafka.execInContainer(
67+
"/bin/sh",
68+
"-c",
69+
"/usr/bin/kafka-topics --bootstrap-server=localhost:9092 --create --topic kafka_topic --partitions 1 --replication-factor 1");
70+
}
71+
72+
@AfterAll
73+
public void tearDown() {
74+
kafka.stop();
75+
}
76+
77+
@Override
78+
protected String getApplicationImageName() {
79+
return "aws-appsignals-tests-kafka-kafka-consumers";
80+
}
81+
82+
@Override
83+
protected String getApplicationWaitPattern() {
84+
return ".*Routes ready.*";
85+
}
86+
87+
@Override
88+
protected Set<String> getExpectedMetrics() {
89+
return ImmutableSet.<String>builder()
90+
.addAll(JMXMetricsConstants.KAFKA_CONSUMER_METRICS_SET)
91+
.addAll(super.getExpectedMetrics())
92+
.build();
93+
}
94+
95+
@Override
96+
protected Map<String, String> getApplicationExtraEnvironmentVariables() {
97+
return Map.of(
98+
"OTEL_JMX_ENABLED", "true",
99+
"OTEL_JMX_TARGET_SYSTEM", "kafka-consumer");
100+
}
101+
}

0 commit comments

Comments
 (0)