Skip to content

Commit d9aa7c1

Browse files
committed
chore(ci): Make E2E asset deployment compatible with updated CDK lib version. Refactor retry4j to resilience4j.
1 parent e6c1676 commit d9aa7c1

File tree

7 files changed

+168
-87
lines changed

7 files changed

+168
-87
lines changed

powertools-e2e-tests/pom.xml

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,12 @@
4040
<artifactId>log4j-slf4j2-impl</artifactId>
4141
<scope>test</scope>
4242
</dependency>
43-
4443
<dependency>
4544
<groupId>software.amazon.awssdk</groupId>
4645
<artifactId>lambda</artifactId>
4746
<version>${aws.sdk.version}</version>
4847
<scope>test</scope>
4948
</dependency>
50-
5149
<dependency>
5250
<groupId>software.amazon.awssdk</groupId>
5351
<artifactId>dynamodb</artifactId>
@@ -66,72 +64,61 @@
6664
<version>${aws.sdk.version}</version>
6765
<scope>test</scope>
6866
</dependency>
69-
7067
<dependency>
7168
<groupId>software.amazon.awssdk</groupId>
7269
<artifactId>xray</artifactId>
7370
<version>${aws.sdk.version}</version>
7471
<scope>test</scope>
7572
</dependency>
76-
7773
<dependency>
7874
<groupId>software.amazon.awssdk</groupId>
7975
<artifactId>sqs</artifactId>
8076
<version>${aws.sdk.version}</version>
8177
<scope>test</scope>
8278
</dependency>
83-
8479
<dependency>
8580
<groupId>com.amazonaws</groupId>
8681
<artifactId>amazon-sqs-java-extended-client-lib</artifactId>
8782
<version>2.1.2</version>
8883
<scope>test</scope>
8984
</dependency>
90-
9185
<dependency>
9286
<groupId>software.amazon.awssdk</groupId>
9387
<artifactId>url-connection-client</artifactId>
9488
<scope>test</scope>
9589
</dependency>
96-
9790
<dependency>
9891
<groupId>org.junit.jupiter</groupId>
9992
<artifactId>junit-jupiter-api</artifactId>
10093
<scope>test</scope>
10194
</dependency>
102-
10395
<dependency>
10496
<groupId>commons-io</groupId>
10597
<artifactId>commons-io</artifactId>
10698
<version>2.20.0</version>
10799
</dependency>
108-
109100
<dependency>
110101
<groupId>org.junit.jupiter</groupId>
111102
<artifactId>junit-jupiter-engine</artifactId>
112103
<scope>test</scope>
113104
</dependency>
114-
115105
<dependency>
116106
<groupId>org.assertj</groupId>
117107
<artifactId>assertj-core</artifactId>
118108
<scope>test</scope>
119109
</dependency>
120-
121110
<dependency>
122-
<groupId>com.evanlennick</groupId>
123-
<artifactId>retry4j</artifactId>
124-
<version>0.15.0</version>
111+
<groupId>io.github.resilience4j</groupId>
112+
<artifactId>resilience4j-retry</artifactId>
113+
<version>2.3.0</version>
125114
<scope>test</scope>
126115
</dependency>
127-
128116
<dependency>
129117
<groupId>software.amazon.awscdk</groupId>
130118
<artifactId>aws-cdk-lib</artifactId>
131119
<version>${cdk.version}</version>
132120
<scope>test</scope>
133121
</dependency>
134-
135122
<dependency>
136123
<groupId>software.constructs</groupId>
137124
<artifactId>constructs</artifactId>

powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.IOException;
2121
import java.nio.file.Paths;
2222
import java.util.HashMap;
23+
import java.util.Iterator;
2324
import java.util.Map;
2425
import java.util.Objects;
2526
import java.util.UUID;
@@ -469,10 +470,18 @@ private Map<String, Asset> findAssets() {
469470
files.iterator().forEachRemaining(file -> {
470471
String assetPath = file.get("source").get("path").asText();
471472
String assetPackaging = file.get("source").get("packaging").asText();
472-
String bucketName = file.get("destinations").get("current_account-current_region").get("bucketName")
473-
.asText();
474-
String objectKey = file.get("destinations").get("current_account-current_region").get("objectKey")
475-
.asText();
473+
JsonNode destinations = file.get("destinations");
474+
String bucketName = null;
475+
String objectKey = null;
476+
Iterator<String> fieldNames = destinations.fieldNames();
477+
while (fieldNames.hasNext()) {
478+
String fieldName = fieldNames.next();
479+
if (fieldName.startsWith("current_account-current_region")) {
480+
bucketName = destinations.get(fieldName).get("bucketName").asText();
481+
objectKey = destinations.get(fieldName).get("objectKey").asText();
482+
break;
483+
}
484+
}
476485
Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account)
477486
.replace("${AWS::Region}", region.toString()));
478487
assets.put(objectKey, asset);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates.
3+
* Licensed under the Apache License, Version 2.0 (the
4+
* "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*
13+
*/
14+
15+
package software.amazon.lambda.powertools.testutils;
16+
17+
import java.time.Duration;
18+
import java.util.function.Supplier;
19+
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import io.github.resilience4j.retry.Retry;
24+
import io.github.resilience4j.retry.RetryConfig;
25+
26+
/**
27+
* Utility class for consistent retry configuration across all test utilities.
28+
*/
29+
public class RetryUtils {
30+
private static final Logger LOG = LoggerFactory.getLogger(RetryUtils.class);
31+
32+
private static final RetryConfig DEFAULT_RETRY_CONFIG = RetryConfig.custom()
33+
.maxAttempts(60) // 60 attempts over 5 minutes
34+
.waitDuration(Duration.ofSeconds(5)) // 5 seconds between attempts
35+
.build();
36+
37+
private RetryUtils() {
38+
// Utility class
39+
}
40+
41+
/**
42+
* Creates a retry instance with default configuration for the specified exception type.
43+
*
44+
* @param name the name for the retry instance
45+
* @param retryOnException the exception class to retry on
46+
* @return configured Retry instance
47+
*/
48+
public static Retry createRetry(String name, Class<? extends Exception> retryOnException) {
49+
RetryConfig config = RetryConfig.from(DEFAULT_RETRY_CONFIG)
50+
.retryExceptions(retryOnException)
51+
.build();
52+
53+
Retry retry = Retry.of(name, config);
54+
retry.getEventPublisher().onRetry(event -> LOG.warn("Retry attempt {} for {}: {}",
55+
event.getNumberOfRetryAttempts(), name, event.getLastThrowable().getMessage()));
56+
57+
return retry;
58+
}
59+
60+
/**
61+
* Decorates a supplier with retry logic for the specified exception type.
62+
*
63+
* @param supplier the supplier to decorate
64+
* @param name the name for the retry instance
65+
* @param retryOnException the exception class to retry on
66+
* @return decorated supplier with retry logic
67+
*/
68+
public static <T> Supplier<T> withRetry(Supplier<T> supplier, String name,
69+
Class<? extends Exception> retryOnException) {
70+
Retry retry = createRetry(name, retryOnException);
71+
return Retry.decorateSupplier(retry, supplier);
72+
}
73+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates.
3+
* Licensed under the Apache License, Version 2.0 (the
4+
* "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*
13+
*/
14+
15+
package software.amazon.lambda.powertools.testutils.metrics;
16+
17+
/**
18+
* Exception thrown when metric data is not found in CloudWatch.
19+
* This exception is used to trigger retries as metrics may not be available immediately.
20+
*/
21+
public class MetricDataNotFoundException extends RuntimeException {
22+
public MetricDataNotFoundException(String message) {
23+
super(message);
24+
}
25+
}

powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,15 @@
1414

1515
package software.amazon.lambda.powertools.testutils.metrics;
1616

17-
import static java.time.Duration.ofSeconds;
18-
19-
import com.evanlennick.retry4j.CallExecutor;
20-
import com.evanlennick.retry4j.CallExecutorBuilder;
21-
import com.evanlennick.retry4j.Status;
22-
import com.evanlennick.retry4j.config.RetryConfig;
23-
import com.evanlennick.retry4j.config.RetryConfigBuilder;
2417
import java.time.Instant;
2518
import java.util.ArrayList;
2619
import java.util.List;
2720
import java.util.Map;
28-
import java.util.concurrent.Callable;
21+
import java.util.function.Supplier;
22+
2923
import org.slf4j.Logger;
3024
import org.slf4j.LoggerFactory;
25+
3126
import software.amazon.awssdk.http.SdkHttpClient;
3227
import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient;
3328
import software.amazon.awssdk.regions.Region;
@@ -39,6 +34,7 @@
3934
import software.amazon.awssdk.services.cloudwatch.model.MetricDataQuery;
4035
import software.amazon.awssdk.services.cloudwatch.model.MetricStat;
4136
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit;
37+
import software.amazon.lambda.powertools.testutils.RetryUtils;
4238

4339
/**
4440
* Class in charge of retrieving the actual metrics of a Lambda execution on CloudWatch
@@ -73,7 +69,7 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String
7369
dimensions.forEach((key, value) -> dimensionsList.add(Dimension.builder().name(key).value(value).build()));
7470
}
7571

76-
Callable<List<Double>> callable = () -> {
72+
Supplier<List<Double>> supplier = () -> {
7773
LOG.debug("Get Metrics for namespace {}, start {}, end {}, metric {}, dimensions {}", namespace, start,
7874
end, metricName, dimensionsList);
7975
GetMetricDataResponse metricData = cloudwatch.getMetricData(GetMetricDataRequest.builder()
@@ -96,24 +92,11 @@ public List<Double> fetchMetrics(Instant start, Instant end, int period, String
9692
.build());
9793
List<Double> values = metricData.metricDataResults().get(0).values();
9894
if (values == null || values.isEmpty()) {
99-
throw new Exception("No data found for metric " + metricName);
95+
throw new MetricDataNotFoundException("No data found for metric " + metricName);
10096
}
10197
return values;
10298
};
10399

104-
RetryConfig retryConfig = new RetryConfigBuilder()
105-
.withMaxNumberOfTries(10)
106-
.retryOnAnyException()
107-
.withDelayBetweenTries(ofSeconds(2))
108-
.withRandomExponentialBackoff()
109-
.build();
110-
CallExecutor<List<Double>> callExecutor = new CallExecutorBuilder<List<Double>>()
111-
.config(retryConfig)
112-
.afterFailedTryListener(s -> {
113-
LOG.warn("{}, attempts: {}", s.getLastExceptionThatCausedRetry().getMessage(), s.getTotalTries());
114-
})
115-
.build();
116-
Status<List<Double>> status = callExecutor.execute(callable);
117-
return status.getResult();
100+
return RetryUtils.withRetry(supplier, "metrics-fetcher-" + metricName, MetricDataNotFoundException.class).get();
118101
}
119102
}

0 commit comments

Comments
 (0)