Skip to content

Commit

Permalink
improve documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromevdl committed Mar 3, 2023
1 parent 841269e commit 7b81e22
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 5 deletions.
7 changes: 5 additions & 2 deletions powertools-e2e-tests/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
## End-to-end tests
This module is internal and meant to be used for end-to-end (E2E) testing of Lambda Powertools for Java.

__Prerequisites__: an AWS account is needed as well as a local environment able to reach this account
__Prerequisites__:
- An AWS account is needed as well as a local environment able to reach this account
([credentials](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials.html)).
- [Java 11+](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html)
- [Docker](https://docs.docker.com/engine/install/)

To execute the E2E tests, use the following command: `mvn clean verify -Pe2e`
To execute the E2E tests, use the following command: `export JAVA_VERSION=11 && mvn clean verify -Pe2e`

### Under the hood
This module leverages the following components:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,22 @@

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Collections.singletonList;

/**
* This class is in charge of bootstrapping the infrastructure for the tests.
* <br/>
* Tests are actually run on AWS, so we need to provision Lambda functions, DynamoDB table (for Idempotency),
* CloudWatch log groups, ...
* <br/>
* It uses the Cloud Development Kit (CDK) to define required resources. The CDK stack is then synthesized to retrieve
* the CloudFormation templates and the assets (function jars). Assets are uploaded to S3 (with the SDK `PutObjectRequest`)
* and the CloudFormation stack is created (with the SDK `createStack`)
*/
public class Infrastructure {
private static final Logger LOG = LoggerFactory.getLogger(Infrastructure.class);

Expand Down Expand Up @@ -92,6 +101,10 @@ private Infrastructure(Builder builder) {
.build();
}

/**
* Use the CloudFormation SDK to create the stack
* @return the name of the function deployed part of the stack
*/
public String deploy() {
uploadAssets();
LOG.info("Deploying '" + stackName + "' on account " + account);
Expand All @@ -111,6 +124,9 @@ public String deploy() {
return functionName;
}

/**
* Destroy the CloudFormation stack
*/
public void destroy() {
LOG.info("Deleting '" + stackName + "' on account " + account);
cfn.deleteStack(DeleteStackRequest.builder().stackName(stackName).build());
Expand Down Expand Up @@ -193,6 +209,10 @@ public Builder timeoutInSeconds(long timeoutInSeconds) {
}
}

/**
* Build the CDK Stack containing the required resources (Lambda function, LogGroup, DDB Table)
* @return the CDK stack
*/
private Stack createStackWithLambda() {
Stack stack = new Stack(app, stackName);
List<String> packagingInstruction = Arrays.asList(
Expand Down Expand Up @@ -261,12 +281,18 @@ private Stack createStackWithLambda() {
return stack;
}

/**
* cdk synth to retrieve the CloudFormation template and assets directory
*/
private void synthesize() {
CloudAssembly synth = app.synth();
cfnTemplate = synth.getStackByName(stack.getStackName()).getTemplate();
cfnAssetDirectory = synth.getDirectory();
}

/**
* Upload assets (mainly lambda function jars) to S3
*/
private void uploadAssets() {
Map<String, Asset> assets = findAssets();
assets.forEach((objectKey, asset) -> {
Expand All @@ -283,6 +309,10 @@ private void uploadAssets() {
});
}

/**
* Reading the cdk assets.json file to retrieve the list of assets to push to S3
* @return a map of assets
*/
private Map<String, Asset> findAssets() {
Map<String, Asset> assets = new HashMap<>();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import java.util.Base64;
import java.util.stream.IntStream;

/**
* Logs for a specific Lambda invocation
*/
public class InvocationLogs {
private String[] logs;
private String[] functionLogs;
private final String[] logs;
private final String[] functionLogs;

public InvocationLogs(String base64Logs, String requestId) {
String rawLogs = new String(Base64.getDecoder().decode(base64Logs), StandardCharsets.UTF_8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
import java.util.concurrent.Callable;

import static java.time.Duration.ofSeconds;

/**
* Class in charge of retrieving the actual metrics of a Lambda execution on CloudWatch
*/
public class MetricsFetcher {
private static final Logger LOG = LoggerFactory.getLogger(MetricsFetcher.class);

Expand All @@ -30,6 +34,17 @@ public class MetricsFetcher {
.region(region)
.build();

/**
* Retrieve the metric values from start to end. Different parameters are required (see {@link CloudWatchClient#getMetricData} for more info).
* Use a retry mechanism as metrics may not be available instantaneously after a function runs.
* @param start
* @param end
* @param period
* @param namespace
* @param metricName
* @param dimensions
* @return
*/
public List<Double> fetchMetrics(Instant start, Instant end, int period, String namespace, String metricName, Map<String, String> dimensions) {
List<Dimension> dimensionsList = new ArrayList<>();
if (dimensions != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

import static java.time.Duration.ofSeconds;

/**
* Class in charge of retrieving the actual traces of a Lambda execution on X-Ray
*/
public class TraceFetcher {

private static final ObjectMapper MAPPER = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Expand All @@ -36,6 +39,12 @@ public class TraceFetcher {
private final String filterExpression;
private final List<String> excludedSegments;

/**
* @param start beginning of the time slot to search in
* @param end end of the time slot to search in
* @param filterExpression eventual filter for the search
* @param excludedSegments list of segment to exclude from the search
*/
public TraceFetcher(Instant start, Instant end, String filterExpression, List<String> excludedSegments) {
this.start = start;
this.end = end;
Expand All @@ -47,6 +56,12 @@ public static Builder builder() {
return new Builder();
}

/**
* Retrieve the traces corresponding to a specific function during a specific time slot.
* Use a retry mechanism as traces may not be available instantaneously after a function runs.
*
* @return traces
*/
public Trace fetchTrace() {
Callable<Trace> callable = () -> {
List<String> traceIds = getTraceIds();
Expand All @@ -67,6 +82,11 @@ public Trace fetchTrace() {
return status.getResult();
}

/**
* Retrieve traces from trace ids.
* @param traceIds
* @return
*/
private Trace getTrace(List<String> traceIds) {
BatchGetTracesResponse tracesResponse = xray.batchGetTraces(BatchGetTracesRequest.builder()
.traceIds(traceIds)
Expand Down Expand Up @@ -110,6 +130,10 @@ private void getNestedSubSegments(List<SubSegment> subsegments, Trace traceRes,
});
}

/**
* Use the X-Ray SDK to retrieve the trace ids corresponding to a specific function during a specific time slot
* @return a list of trace ids
*/
private List<String> getTraceIds() {
GetTraceSummariesResponse traceSummaries = xray.getTraceSummaries(GetTraceSummariesRequest.builder()
.startTime(start)
Expand Down

0 comments on commit 7b81e22

Please sign in to comment.