Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/maven.yml → .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ jobs:
distribution: temurin
cache: maven
- name: Check formatting
run: ./mvnw --batch-mode fmt:check
- name: Build
run: ./mvnw --batch-mode package
run: ./mvnw -B fmt:check
- name: Build and verify
run: ./mvnw -B verify
- name: Update dependency graph
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: advanced-security/maven-dependency-submission-action@b275d12641ac2d2108b2cbb7598b154ad2f2cee8 # v5.0.0
Expand Down
2 changes: 1 addition & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ It usually appears immediately after the release process is done, but can take a
## Docker images

As part of the release process, `./mvnw` will create the git tag.
This tag is picked up by [GitHub Actions](https://github.com/prometheus/cloudwatch_exporter/actions/workflows/maven.yml), which builds and pushes the [Docker images](README.md#docker-images).
This tag is picked up by [GitHub Actions](https://github.com/prometheus/cloudwatch_exporter/actions/workflows/build.yml), which builds and pushes the [Docker images](README.md#docker-images).

## GitHub Release

Expand Down
50 changes: 41 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
Expand All @@ -50,11 +50,13 @@
<project.releaseDate>${maven.build.timestamp}</project.releaseDate>
<maven.compiler.release>17</maven.compiler.release>
<!-- Dependency versions -->
<assertj.version>3.27.7</assertj.version>
<caffeine.version>3.2.4</caffeine.version>
<commons-codec.version>1.22.0</commons-codec.version>
<hamcrest.version>3.0</hamcrest.version>
<io.prometheus.version>0.16.0</io.prometheus.version>
<junit.version>4.13.2</junit.version>
<jetty.version>12.1.10</jetty.version>
<junit.jupiter.version>6.1.0</junit.jupiter.version>
<mockito.version>5.23.0</mockito.version>
<slf4j.version>2.0.18</slf4j.version>
<snakeyaml.version>2.6</snakeyaml.version>
Expand All @@ -72,6 +74,8 @@
<maven-source-plugin.version>3.4.0</maven-source-plugin.version>
<maven-surefire-plugin.version>3.5.6</maven-surefire-plugin.version>
<versions-maven-plugin.version>2.21.0</versions-maven-plugin.version>
<jacoco-maven-plugin.version>0.8.14</jacoco-maven-plugin.version>

</properties>
<dependencies>

Expand Down Expand Up @@ -123,12 +127,12 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>12.0.36</version>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-servlet</artifactId>
<version>12.0.36</version>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
Expand All @@ -137,9 +141,15 @@
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -246,9 +256,31 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>-javaagent:${settings.localRepository}/org/mockito/mockito-core/5.23.0/mockito-core-5.23.0.jar -XX:+EnableDynamicAgentLoading -Djdk.net.URLClassPath.disableClassPathURLCheck=true</argLine>
<useModulePath>false</useModulePath>
<argLine>-javaagent:${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar @{argLine} -XX:+EnableDynamicAgentLoading -Djdk.net.URLClassPath.disableClassPathURLCheck=true</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-maven-plugin.version}</version>
<executions>
<execution>
<id>jacoco-prepare-agent</id>
<phase>process-test-classes</phase>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
Expand Down Expand Up @@ -367,4 +399,4 @@
</build>
</profile>
</profiles>
</project>
</project>
106 changes: 106 additions & 0 deletions scripts/stress-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
#
# Copyright (c) The Prometheus Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_DIR
readonly PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
readonly MVNW="${PROJECT_DIR}/mvnw"

readonly LOG_FILE="stress-test.log"

usage() {
cat <<'EOF'
Usage: ./scripts/stress-test.sh <number-of-iterations>

Run CloudWatch Exporter tests multiple times to check for flaky tests.

Arguments:
<number-of-iterations> Number of times to run the test suite.

Examples:
./scripts/stress-test.sh 5
./scripts/stress-test.sh 10
EOF
}

log() {
echo "[INFO] $*" | tee -a "${LOG_FILE}"
}

fail() {
echo "[ERROR] $*" | tee -a "${LOG_FILE}" >&2
exit 1
}

main() {
local iterations iteration start_time end_time elapsed_time total_elapsed

if [[ $# -ne 1 ]]; then
usage
exit 1
fi

iterations="$1"

[[ -x "${MVNW}" ]] || fail "mvnw not found or not executable: ${MVNW}"

if [[ ! "${iterations}" =~ ^[1-9][0-9]*$ ]]; then
fail "Invalid iterations '${iterations}'. Must be a positive integer."
fi

rm -f "${LOG_FILE}"

log "Starting stress test with ${iterations} iteration(s)"
log ""

log "Building project (initial build)..."
local build_start
build_start="${SECONDS}"
if ! (cd "${PROJECT_DIR}" && ./mvnw -B compile -DskipTests) |& tee -a "${LOG_FILE}"; then
fail "Initial build failed. See ${LOG_FILE} for details."
fi
local build_elapsed
build_elapsed=$((SECONDS - build_start))
log "Initial build completed in ${build_elapsed}s"
log ""

log "Running ${iterations} test iteration(s)..."
log ""

for ((iteration = 1; iteration <= iterations; iteration++)); do
start_time="${SECONDS}"
log "Iteration ${iteration}/${iterations} started"

if ! (cd "${PROJECT_DIR}" && ./mvnw -B test) |& tee -a "${LOG_FILE}"; then
end_time="${SECONDS}"
elapsed_time=$((end_time - start_time))
fail "Iteration ${iteration}/${iterations} failed after ${elapsed_time}s. See ${LOG_FILE} for details."
fi

end_time="${SECONDS}"
elapsed_time=$((end_time - start_time))
log "Iteration ${iteration}/${iterations} passed (${elapsed_time}s)"
log ""
done

total_elapsed="${SECONDS}"

log "All ${iterations} iteration(s) completed successfully in ${total_elapsed}s"
}

main "$@"
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public CloudWatchCollector(String yamlConfig) {
}

/* For unittests. */
@SuppressWarnings("unchecked")
protected CloudWatchCollector(
String jsonConfig,
CloudWatchClient cloudWatchClient,
Expand Down Expand Up @@ -141,6 +142,7 @@ protected void reloadConfig() throws IOException {
}
}

@SuppressWarnings("unchecked")
protected void loadConfig(
Reader in, CloudWatchClient cloudWatchClient, ResourceGroupsTaggingApiClient taggingClient) {
loadConfig(
Expand All @@ -149,6 +151,7 @@ protected void loadConfig(
taggingClient);
}

@SuppressWarnings("unchecked")
private void loadConfig(
Map<String, Object> config,
CloudWatchClient cloudWatchClient,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.prometheus.cloudwatch;

import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;

import io.prometheus.cloudwatch.CachingDimensionSource.DimensionCacheKey;
import io.prometheus.cloudwatch.CachingDimensionSource.DimensionExpiry;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.jupiter.api.Test;

public class CachingDimensionExpiryTest {

Expand All @@ -27,7 +27,7 @@ public void expireAfterCreateUsesDefaultWithEmptyOverrides() {
emptyData,
Instant.now().toEpochMilli());

assertEquals(35, Duration.ofNanos(afterCreate).toSeconds());
assertThat(Duration.ofNanos(afterCreate).toSeconds()).isEqualTo(35);
}

@Test
Expand All @@ -42,7 +42,7 @@ public void expireAfterCreateUsesMetricLevelOverride() {
emptyData,
Instant.now().toEpochMilli());

assertEquals(100, Duration.ofNanos(afterCreate).toSeconds());
assertThat(Duration.ofNanos(afterCreate).toSeconds()).isEqualTo(100);
}

@Test
Expand All @@ -57,7 +57,7 @@ public void expireAfterCreateUsesDefaultIfNoMatchedOverride() {
emptyData,
Instant.now().toEpochMilli());

assertEquals(35, Duration.ofNanos(afterCreate).toSeconds());
assertThat(Duration.ofNanos(afterCreate).toSeconds()).isEqualTo(35);
}

@Test
Expand All @@ -73,7 +73,7 @@ public void expireAfterUpdateUsesCurrentDuration() {
Instant.now().toEpochMilli(),
10_000_000);

assertEquals(10_000_000, afterUpdate);
assertThat(afterUpdate).isEqualTo(10_000_000);
}

@Test
Expand All @@ -88,7 +88,30 @@ public void expireAfterReadUsesCurrentDuration() {
emptyData,
Instant.now().toEpochMilli(),
20_000_000);
assertEquals(20_000_000, afterRead);
assertThat(afterRead).isEqualTo(20_000_000);
}

@Test
public void dimensionCacheKeyEqualsHandlesIdentityNullDifferentTypesAndFields() {
DimensionCacheKey key = createDimensionCacheKey("AWS/S3", "BucketSizeBytes", 100);
DimensionCacheKey same = createDimensionCacheKey("AWS/S3", "BucketSizeBytes", 100);
DimensionCacheKey differentRule = createDimensionCacheKey("AWS/EC2", "CPUUtilization", 100);
DimensionCacheKey differentTags =
new DimensionCacheKey(
createMetricRule("AWS/S3", "BucketSizeBytes", 100), List.of("bucket-a"));

assertThat(key).isEqualTo(key);
assertThat(key).isEqualTo(same);
assertThat(key).isNotEqualTo(null);
assertThat(key).isNotEqualTo("not a key");
assertThat(key).isNotEqualTo(differentRule);
assertThat(key).isNotEqualTo(differentTags);
assertThat(key.hashCode()).isEqualTo(same.hashCode());
}

@Test
public void dimensionCacheKeyHashCodeHandlesNullFields() {
assertThat(new DimensionCacheKey(null, null).hashCode()).isZero();
}

private DimensionCacheKey createDimensionCacheKey(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package io.prometheus.cloudwatch;

import static io.prometheus.cloudwatch.DimensionSource.DimensionData;
import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;

import io.prometheus.cloudwatch.CachingDimensionSource.DimensionCacheConfig;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.services.cloudwatch.model.Dimension;

public class CachingDimensionSourceTest {
Expand All @@ -24,8 +24,8 @@ public void cachedFromDelegate() {
sut.getDimensions(createMetricRule("AWS/Redshift", "WriteIOPS"), Collections.emptyList());

Dimension dimension = Dimension.builder().name("AWS/Redshift").value("WriteIOPS").build();
assertEquals(1, source.called);
assertEquals(dimension, expected.getDimensions().get(0).get(0));
assertThat(source.called).isEqualTo(1);
assertThat(expected.getDimensions().get(0).get(0)).isEqualTo(dimension);
}

private MetricRule createMetricRule(String namespace, String name) {
Expand Down
Loading
Loading