-
Notifications
You must be signed in to change notification settings - Fork 241
Add end-to-end integration tests #619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # this workflow verifies that the integration test Lambda function builds successfully. | ||
| # it does NOT deploy or run the tests (that requires AWS credentials and is done in | ||
| # run-integration-test.yml). | ||
|
|
||
| name: Build integration tests | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main ] | ||
| paths: | ||
| - 'aws-lambda-java-log4j2/**' | ||
| - 'aws-lambda-java-core/**' | ||
| - 'lambda-integration-tests/**' | ||
| pull_request: | ||
| branches: [ '*' ] | ||
| paths: | ||
| - 'aws-lambda-java-log4j2/**' | ||
| - 'aws-lambda-java-core/**' | ||
| - 'lambda-integration-tests/**' | ||
| - '.github/workflows/build-integration-test.yml' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
|
|
||
| - name: Set up JDK | ||
| uses: actions/setup-java@v5 | ||
| with: | ||
| java-version: | | ||
| 8 | ||
| 21 | ||
| distribution: corretto | ||
| cache: maven | ||
|
|
||
| - name: Install core with Maven | ||
| run: | | ||
| export JAVA_HOME=$JAVA_HOME_8_X64 | ||
| mvn -B install --file aws-lambda-java-core/pom.xml | ||
| - name: Install log4j2 with Maven | ||
| run: | | ||
| export JAVA_HOME=$JAVA_HOME_8_X64 | ||
| mvn -B install --file aws-lambda-java-log4j2/pom.xml | ||
| # build the integration test function | ||
| # this verifies that the function compiles and packages correctly. | ||
| # the tests will run in run-integration-test.yml which deploys to AWS. | ||
| - name: Package integration test function | ||
| run: | | ||
| export JAVA_HOME=$JAVA_HOME_21_X64 | ||
| mvn -B package --file lambda-integration-tests/log4j2-test-function/pom.xml | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| # this workflow deploys a Lambda function that uses aws-lambda-java-log4j2, | ||
| # invokes it, and verifies that logs arrive in CloudWatch. | ||
|
|
||
| name: Run integration tests | ||
|
|
||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| branches: [ main ] | ||
| paths: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here the test should be run for most packages, at least the one that are involved. core, serialization, runtime-interface-client, log4j and i would also add events. |
||
| - 'aws-lambda-java-log4j2/**' | ||
| - 'aws-lambda-java-core/**' | ||
| - 'lambda-integration-tests/**' | ||
|
|
||
| jobs: | ||
| run-integration-tests: | ||
| # Only run on the main repo, not forks | ||
| if: ${{ github.repository_owner == 'aws' }} | ||
| runs-on: ubuntu-latest | ||
| concurrency: | ||
| group: integration-test | ||
| cancel-in-progress: false | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add the commit sha to prevent supply chain attacks. |
||
|
|
||
| - name: Set up JDK | ||
| uses: actions/setup-java@v5 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here and in all other references to github actions. |
||
| with: | ||
| java-version: | | ||
| 8 | ||
| 21 | ||
| distribution: corretto | ||
| cache: maven | ||
|
|
||
| - name: Install SAM CLI | ||
| uses: aws-actions/setup-sam@v2 | ||
| with: | ||
| use-installer: true | ||
|
|
||
| - name: Configure AWS credentials | ||
| uses: aws-actions/configure-aws-credentials@v6.0.0 | ||
| with: | ||
| role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | ||
| role-session-name: ${{ secrets.ROLE_SESSION_NAME }} | ||
| aws-region: ${{ secrets.AWS_REGION }} | ||
|
|
||
| - name: Install core with Maven | ||
| run: | | ||
| export JAVA_HOME=$JAVA_HOME_8_X64 | ||
| mvn -B install --file aws-lambda-java-core/pom.xml | ||
| - name: Install log4j2 with Maven | ||
| run: | | ||
| export JAVA_HOME=$JAVA_HOME_8_X64 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably correct here. But it is not correct in general. In Java we have some C++ component that are dependent on the architecture. We should have a look at that together. The test should be run per arch and probably runtime (I was doing a CR also for that). For the moment it's probably fine. We should also do this #617 |
||
| mvn -B install --file aws-lambda-java-log4j2/pom.xml | ||
| - name: Build SAM stack | ||
| run: | | ||
| export JAVA_HOME=$JAVA_HOME_21_X64 | ||
| cd lambda-integration-tests && sam build | ||
| - name: Validate SAM stack | ||
| run: cd lambda-integration-tests && sam validate --lint | ||
|
|
||
| - name: Deploy stack | ||
| id: deploy_stack | ||
| env: | ||
| AWS_REGION: ${{ secrets.AWS_REGION }} | ||
| run: | | ||
| cd lambda-integration-tests | ||
| stackName="aws-lambda-java-log4j2-integ-test-$GITHUB_RUN_ID" | ||
| echo "STACK_NAME=$stackName" >> "$GITHUB_OUTPUT" | ||
| echo "Stack name = $stackName" | ||
| sam deploy \ | ||
| --stack-name "${stackName}" \ | ||
| --parameter-overrides "ParameterKey=LambdaRole,ParameterValue=${{ secrets.AWS_LAMBDA_ROLE }}" \ | ||
| --no-confirm-changeset \ | ||
| --no-progressbar \ | ||
| --resolve-s3 \ | ||
| --capabilities CAPABILITY_IAM \ | ||
| 2>&1 | tee /tmp/sam-deploy.log | tail -n 20 | ||
| LOG4J2_TEST_FUNCTION=$(sam list stack-outputs --stack-name "${stackName}" --output json | jq -r '.[] | select(.OutputKey=="Log4j2TestFunction") | .OutputValue') | ||
| echo "LOG4J2_TEST_FUNCTION=$LOG4J2_TEST_FUNCTION" >> "$GITHUB_OUTPUT" | ||
| echo "Function name: $LOG4J2_TEST_FUNCTION" | ||
| - name: Run integration test | ||
| env: | ||
| LOG4J2_TEST_FUNCTION: ${{ steps.deploy_stack.outputs.LOG4J2_TEST_FUNCTION }} | ||
| AWS_REGION: ${{ secrets.AWS_REGION }} | ||
| run: ./lambda-integration-tests/run-tests.sh | ||
|
|
||
| - name: Cleanup | ||
| if: always() && steps.deploy_stack.outputs.STACK_NAME | ||
| env: | ||
| AWS_REGION: ${{ secrets.AWS_REGION }} | ||
| STACK_NAME: ${{ steps.deploy_stack.outputs.STACK_NAME }} | ||
| run: | | ||
| sam delete --stack-name "${STACK_NAME}" --no-prompts --region "${AWS_REGION}" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <groupId>com.amazonaws</groupId> | ||
| <artifactId>log4j2-integration-test-function</artifactId> | ||
| <version>1.0.0</version> | ||
| <packaging>jar</packaging> | ||
|
|
||
| <name>Log4j2 Integration Test Function</name> | ||
| <description> | ||
| Lambda function used to verify that aws-lambda-java-log4j2 correctly emits logs to CloudWatch. | ||
| </description> | ||
|
|
||
| <properties> | ||
| <maven.compiler.source>21</maven.compiler.source> | ||
| <maven.compiler.target>21</maven.compiler.target> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| <log4j.version>2.25.4</log4j.version> | ||
| </properties> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>com.amazonaws</groupId> | ||
| <artifactId>aws-lambda-java-core</artifactId> | ||
| <version>1.4.0</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.amazonaws</groupId> | ||
| <artifactId>aws-lambda-java-log4j2</artifactId> | ||
| <version>1.6.4</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.apache.logging.log4j</groupId> | ||
| <artifactId>log4j-core</artifactId> | ||
| <version>${log4j.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.apache.logging.log4j</groupId> | ||
| <artifactId>log4j-api</artifactId> | ||
| <version>${log4j.version}</version> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-shade-plugin</artifactId> | ||
| <version>3.6.1</version> | ||
| <executions> | ||
| <execution> | ||
| <phase>package</phase> | ||
| <goals> | ||
| <goal>shade</goal> | ||
| </goals> | ||
| <configuration> | ||
| <transformers> | ||
| <transformer | ||
| implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer"> | ||
| </transformer> | ||
| </transformers> | ||
| </configuration> | ||
| </execution> | ||
| </executions> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>com.github.edwgiz</groupId> | ||
| <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> | ||
| <version>2.8.1</version> | ||
| </dependency> | ||
| </dependencies> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package integ; | ||
|
|
||
| import com.amazonaws.services.lambda.runtime.Context; | ||
| import com.amazonaws.services.lambda.runtime.RequestHandler; | ||
| import org.apache.logging.log4j.LogManager; | ||
| import org.apache.logging.log4j.Logger; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| /** | ||
| * integration test handler that logs a marker string using Log4j2 with the LambdaAppender. | ||
| * the test verifies that the marker appears in CloudWatch Logs, confirming end-to-end | ||
| * log delivery through the aws-lambda-java-log4j2 library. | ||
| */ | ||
| public class Log4j2TestHandler implements RequestHandler<Map<String, String>, String> { | ||
|
|
||
| private static final Logger logger = LogManager.getLogger(Log4j2TestHandler.class); | ||
|
|
||
| @Override | ||
| public String handleRequest(Map<String, String> event, Context context) { | ||
| String marker = event.getOrDefault("marker", "NO_MARKER_PROVIDED"); | ||
|
|
||
| logger.info("INTEG_TEST_MARKER: {}", marker); | ||
| logger.debug("Debug level message with marker: {}", marker); | ||
| logger.warn("Warning level message with marker: {}", marker); | ||
| logger.error("Error level message with marker: {}", marker); | ||
|
|
||
| return "OK:" + marker; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <Configuration status="WARN"> | ||
| <Appenders> | ||
| <Lambda name="Lambda" format="${env:AWS_LAMBDA_LOG_FORMAT:-TEXT}"> | ||
| <LambdaTextFormat> | ||
| <PatternLayout> | ||
| <pattern>%d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1}:%L - %m%n</pattern> | ||
| </PatternLayout> | ||
| </LambdaTextFormat> | ||
| </Lambda> | ||
| </Appenders> | ||
| <Loggers> | ||
| <Root level="DEBUG"> | ||
| <AppenderRef ref="Lambda" /> | ||
| </Root> | ||
| </Loggers> | ||
| </Configuration> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| # integration test script for aws-lambda-java-log4j2. | ||
| # invokes the deployed lambda function and verifies logs appear in CloudWatch. | ||
|
|
||
| set -euo pipefail | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the moment it's sufficient like this but we probably can do something more advanced. For example we can create a Java project that uses a something like JUNIT to run the AWS SDK invoke the function and then get the logs. In this way we avoid doing scripting and we can also have a nice report for how the test has gone. |
||
|
|
||
| FUNCTION_NAME="${LOG4J2_TEST_FUNCTION:?LOG4J2_TEST_FUNCTION env var is required}" | ||
| REGION="${AWS_REGION:?AWS_REGION env var is required}" | ||
| MARKER="integ-test-$(date +%s)-${RANDOM}" | ||
|
|
||
| echo "=== Log4j2 Integration Test ===" | ||
| echo "Function: ${FUNCTION_NAME}" | ||
| echo "Region: ${REGION}" | ||
| echo "Marker: ${MARKER}" | ||
| echo "" | ||
|
|
||
| # invoke the lambda function | ||
| echo ">>> Invoking Lambda function..." | ||
| INVOKE_OUTPUT=$(aws lambda invoke \ | ||
| --function-name "${FUNCTION_NAME}" \ | ||
| --region "${REGION}" \ | ||
| --payload "{\"marker\": \"${MARKER}\"}" \ | ||
| --cli-binary-format raw-in-base64-out \ | ||
| --output json \ | ||
| /tmp/integ-test-response.json) || { | ||
| echo "FAIL: aws lambda invoke command failed with exit code $?" | ||
| echo "Output: ${INVOKE_OUTPUT:-<empty>}" | ||
| exit 1 | ||
| } | ||
|
|
||
| echo "Invoke output: ${INVOKE_OUTPUT}" | ||
| RESPONSE=$(cat /tmp/integ-test-response.json) | ||
| echo "Response payload: ${RESPONSE}" | ||
|
|
||
| # check for lambda execution errors | ||
| FUNCTION_ERROR=$(echo "${INVOKE_OUTPUT}" | jq -r '.FunctionError // empty') | ||
| if [ -n "${FUNCTION_ERROR}" ]; then | ||
| echo "FAIL: Lambda function returned an execution error (FunctionError: ${FUNCTION_ERROR})" | ||
| echo "Error response: ${RESPONSE}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # verify the function executed successfully | ||
| if echo "${RESPONSE}" | grep -q "OK:${MARKER}"; then | ||
| echo ">>> Function invocation successful." | ||
| else | ||
| echo "FAIL: Unexpected response from Lambda function." | ||
| echo "Expected response containing: OK:${MARKER}" | ||
| echo "Got: ${RESPONSE}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # query CloudWatch logs for the marker | ||
| LOG_GROUP="/aws/lambda/${FUNCTION_NAME}" | ||
| echo "" | ||
| echo ">>> Querying CloudWatch Logs group: ${LOG_GROUP}" | ||
|
|
||
| MAX_ATTEMPTS=5 | ||
| WAIT_SECONDS=10 | ||
| FOUND=false | ||
|
|
||
| for attempt in $(seq 1 $MAX_ATTEMPTS); do | ||
| echo ">>> Attempt ${attempt}/${MAX_ATTEMPTS}: waiting ${WAIT_SECONDS}s for log propagation..." | ||
| sleep "${WAIT_SECONDS}" | ||
|
|
||
| LOGS_OUTPUT=$(aws logs filter-log-events \ | ||
| --log-group-name "${LOG_GROUP}" \ | ||
| --region "${REGION}" \ | ||
| --filter-pattern "\"INTEG_TEST_MARKER\" \"${MARKER}\"" \ | ||
| --start-time $(($(date +%s) * 1000 - 120000)) \ | ||
| --output json 2>&1) | ||
|
|
||
| if echo "${LOGS_OUTPUT}" | grep -q "INTEG_TEST_MARKER: ${MARKER}"; then | ||
| FOUND=true | ||
| break | ||
| fi | ||
|
|
||
| echo " Marker not found yet." | ||
| WAIT_SECONDS=$((WAIT_SECONDS * 2)) | ||
| done | ||
|
|
||
| # verify the marker was found | ||
| if [ "${FOUND}" = true ]; then | ||
| echo "" | ||
| echo "=== PASS: Log4j2 integration test succeeded ===" | ||
| echo "The marker '${MARKER}' was found in CloudWatch Logs (attempt ${attempt})." | ||
| echo "This confirms that the LambdaAppender plugin was discovered by Log4j2" | ||
| echo "and logs are being delivered to CloudWatch correctly." | ||
| else | ||
| echo "" | ||
| echo "=== FAIL: Log4j2 integration test failed ===" | ||
| echo "The marker '${MARKER}' was NOT found in CloudWatch Logs after ${MAX_ATTEMPTS} attempts." | ||
| echo "This indicates that the LambdaAppender was not discovered by Log4j2," | ||
| echo "likely due to a missing Log4j2Plugins.dat in the packaged JAR." | ||
| echo "" | ||
| echo "Dumping all recent log events for debugging:" | ||
| aws logs filter-log-events \ | ||
| --log-group-name "${LOG_GROUP}" \ | ||
| --region "${REGION}" \ | ||
| --start-time $(($(date +%s) * 1000 - 120000)) \ | ||
| --limit 50 \ | ||
| --output text 2>&1 || true | ||
| exit 1 | ||
| fi | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't those supposed to fail as well in the run-integration-test. It seems to me that those test s are rebuilt also there.