Skip to content

Commit f1f2dc8

Browse files
[App Signals] E2E Testing: EC2 Use Case (#615)
1 parent 096b4ba commit f1f2dc8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2002
-44
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## This workflow aims to run the Application Signals end-to-end tests as a canary to
2+
## test the artifacts for App Signals enablement. It will deploy a sample app and remote
3+
## service on two EC2 instances, call the APIs, and validate the generated telemetry,
4+
## including logs, metrics, and traces.
5+
name: App Signals Enablement - E2E EC2 Canary Testing
6+
on:
7+
schedule:
8+
- cron: '0/15 * * * *' # run the workflow every 15 minutes
9+
workflow_dispatch: # be able to run the workflow on demand
10+
11+
permissions:
12+
id-token: write
13+
contents: read
14+
15+
jobs:
16+
e2e-canary-test:
17+
uses: ./.github/workflows/appsignals-e2e-ec2-test.yml
18+
secrets: inherit
19+
with:
20+
caller-workflow-name: 'appsignals-e2e-ec2-canary-test'
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# This is a reusable workflow for running the E2E test for App Signals.
2+
# It is meant to be called from another workflow.
3+
# Read more about reusable workflows: https://docs.github.com/en/actions/using-workflows/reusing-workflows#overview
4+
name: App Signals Enablement E2E Testing - EC2 Use Case
5+
on:
6+
workflow_call:
7+
inputs:
8+
caller-workflow-name:
9+
required: true
10+
type: string
11+
12+
permissions:
13+
id-token: write
14+
contents: read
15+
16+
env:
17+
AWS_DEFAULT_REGION: us-east-1
18+
TEST_ACCOUNT: ${{ secrets.APP_SIGNALS_E2E_TEST_ACC }}
19+
SAMPLE_APP_FRONTEND_SERVICE_JAR: "s3://aws-appsignals-sample-app/main-service.jar"
20+
SAMPLE_APP_REMOTE_SERVICE_JAR: "s3://aws-appsignals-sample-app/remote-service.jar"
21+
APP_SIGNALS_CW_AGENT_RPM: "https://amazoncloudwatch-agent-us-east-1.s3.amazonaws.com/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm"
22+
APP_SIGNALS_ADOT_JAR: "https://github.com/aws-observability/aws-otel-java-instrumentation/releases/latest/download/aws-opentelemetry-agent.jar"
23+
METRIC_NAMESPACE: AppSignals
24+
LOG_GROUP_NAME: /aws/appsignals/generic
25+
26+
jobs:
27+
e2e-ec2-test:
28+
runs-on: ubuntu-latest
29+
steps:
30+
- uses: actions/checkout@v4
31+
with:
32+
fetch-depth: 0
33+
34+
- name: Generate testing id
35+
run: echo TESTING_ID="${{ github.run_id }}-${{ github.run_number }}" >> $GITHUB_ENV
36+
37+
- name: Configure AWS Credentials
38+
uses: aws-actions/configure-aws-credentials@v4
39+
with:
40+
role-to-assume: ${{ secrets.E2E_TEST_ROLE_ARN }}
41+
aws-region: ${{ env.AWS_DEFAULT_REGION }}
42+
43+
- name: Set up terraform
44+
uses: hashicorp/setup-terraform@v2
45+
with:
46+
terraform_wrapper: false
47+
48+
- name: Deploy sample app via terraform
49+
working-directory: testing/terraform/ec2
50+
run: |
51+
terraform init
52+
terraform validate
53+
terraform apply -auto-approve \
54+
-var="aws_region=${{ env.AWS_DEFAULT_REGION }}" \
55+
-var="test_id=${{ env.TESTING_ID }}" \
56+
-var="sample_app_jar=${{ env.SAMPLE_APP_FRONTEND_SERVICE_JAR }}" \
57+
-var="sample_remote_app_jar=${{ env.SAMPLE_APP_REMOTE_SERVICE_JAR }}" \
58+
-var="cw_agent_rpm=${{ env.APP_SIGNALS_CW_AGENT_RPM }}" \
59+
-var="adot_jar=${{ env.APP_SIGNALS_ADOT_JAR }}"
60+
61+
- name: Get the sample app endpoint
62+
run: |
63+
echo "MAIN_SERVICE_ENDPOINT=$(terraform output sample_app_main_service_public_dns):8080" >> $GITHUB_ENV
64+
echo "REMOTE_SERVICE_IP=$(terraform output sample_app_remote_service_public_ip)" >> $GITHUB_ENV
65+
working-directory: testing/terraform/ec2
66+
67+
- name: Wait for app endpoint to come online
68+
id: endpoint-check
69+
run: |
70+
attempt_counter=0
71+
max_attempts=30
72+
until $(curl --output /dev/null --silent --head --fail http://${{ env.MAIN_SERVICE_ENDPOINT }}); do
73+
if [ ${attempt_counter} -eq ${max_attempts} ];then
74+
echo "Max attempts reached"
75+
exit 1
76+
fi
77+
78+
printf '.'
79+
attempt_counter=$(($attempt_counter+1))
80+
sleep 10
81+
done
82+
83+
# This steps increases the speed of the validation by creating the telemetry data in advance
84+
- name: Call all test APIs
85+
continue-on-error: true
86+
run: |
87+
curl -S -s -o /dev/null http://${{ env.MAIN_SERVICE_ENDPOINT }}/outgoing-http-call/
88+
curl -S -s -o /dev/null http://${{ env.MAIN_SERVICE_ENDPOINT }}/aws-sdk-call/
89+
curl -S -s -o /dev/null http://${{ env.MAIN_SERVICE_ENDPOINT }}/remote-service?ip=${{ env.REMOTE_SERVICE_IP }}/
90+
curl -S -s -o /dev/null http://${{ env.MAIN_SERVICE_ENDPOINT }}/client-call/
91+
92+
# Validation for pulse telemetry data
93+
- name: Validate generated EMF logs
94+
id: log-validation
95+
run: ./gradlew testing:validator:run --args='-c ec2/log-validation.yml
96+
--testing-id ${{ env.TESTING_ID }}
97+
--endpoint http://${{ env.MAIN_SERVICE_ENDPOINT }}
98+
--remote-service-deployment-name ${{ env.REMOTE_SERVICE_IP }}:8080
99+
--region ${{ env.AWS_DEFAULT_REGION }}
100+
--account-id ${{ env.TEST_ACCOUNT }}
101+
--metric-namespace ${{ env.METRIC_NAMESPACE }}
102+
--log-group ${{ env.LOG_GROUP_NAME }}
103+
--service-name sample-application-${{ env.TESTING_ID }}
104+
--remote-service-name sample-remote-application-${{ env.TESTING_ID }}
105+
--request-body ip=${{ env.REMOTE_SERVICE_IP }}
106+
--rollup'
107+
108+
- name: Validate generated metrics
109+
id: metric-validation
110+
if: (success() || steps.log-validation.outcome == 'failure') && !cancelled()
111+
run: ./gradlew testing:validator:run --args='-c ec2/metric-validation.yml
112+
--testing-id ${{ env.TESTING_ID }}
113+
--endpoint http://${{ env.MAIN_SERVICE_ENDPOINT }}
114+
--remote-service-deployment-name ${{ env.REMOTE_SERVICE_IP }}:8080
115+
--region ${{ env.AWS_DEFAULT_REGION }}
116+
--account-id ${{ env.TEST_ACCOUNT }}
117+
--metric-namespace ${{ env.METRIC_NAMESPACE }}
118+
--log-group ${{ env.LOG_GROUP_NAME }}
119+
--service-name sample-application-${{ env.TESTING_ID }}
120+
--remote-service-name sample-remote-application-${{ env.TESTING_ID }}
121+
--request-body ip=${{ env.REMOTE_SERVICE_IP }}
122+
--rollup'
123+
124+
- name: Validate generated traces
125+
id: trace-validation
126+
if: (success() || steps.log-validation.outcome == 'failure' || steps.metric-validation.outcome == 'failure') && !cancelled()
127+
run: ./gradlew testing:validator:run --args='-c ec2/trace-validation.yml
128+
--testing-id ${{ env.TESTING_ID }}
129+
--endpoint http://${{ env.MAIN_SERVICE_ENDPOINT }}
130+
--remote-service-deployment-name ${{ env.REMOTE_SERVICE_IP }}:8080
131+
--region ${{ env.AWS_DEFAULT_REGION }}
132+
--account-id ${{ env.TEST_ACCOUNT }}
133+
--metric-namespace ${{ env.METRIC_NAMESPACE }}
134+
--log-group ${{ env.LOG_GROUP_NAME }}
135+
--service-name sample-application-${{ env.TESTING_ID }}
136+
--remote-service-name sample-remote-application-${{ env.TESTING_ID }}
137+
--request-body ip=${{ env.REMOTE_SERVICE_IP }}
138+
--rollup'
139+
140+
- name: Publish metric on test result
141+
if: always()
142+
run: |
143+
if [[ "${{ steps.log-validation.outcome }}" == "success" && "${{ steps.metric-validation.outcome }}" == "success" && "${{ steps.trace-validation.outcome }}" == "success" ]]; then
144+
aws cloudwatch put-metric-data --namespace 'ADOT/GitHubActions' \
145+
--metric-name Failure \
146+
--dimensions repository=${{ github.repository }},branch=${{ github.ref_name }},workflow=${{ inputs.caller-workflow-name }} \
147+
--value 0.0 \
148+
--region ${{ env.AWS_DEFAULT_REGION }}
149+
else
150+
aws cloudwatch put-metric-data --namespace 'ADOT/GitHubActions' \
151+
--metric-name Failure \
152+
--dimensions repository=${{ github.repository }},branch=${{ github.ref_name }},workflow=${{ inputs.caller-workflow-name }} \
153+
--value 1.0 \
154+
--region ${{ env.AWS_DEFAULT_REGION }}
155+
fi
156+
157+
158+
# Clean up Procedures
159+
160+
- name: Terraform destroy
161+
if: always()
162+
continue-on-error: true
163+
working-directory: testing/terraform/ec2
164+
run: |
165+
terraform destroy -auto-approve \
166+
-var="test_id=${{ env.TESTING_ID }}"

.github/workflows/appsignals-e2e-eks-canary-test.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
## This workflow aims to run the end-to-end tests in canary fashion to
2-
## test the prod artifacts for App Signals enablement.
1+
## This workflow aims to run the Application Signals end-to-end tests as a canary to
2+
## test the artifacts for App Signals enablement. It will deploy a sample app and remote
3+
## service onto an EKS cluster, call the APIs, and validate the generated telemetry,
4+
## including logs, metrics, and traces.
35
name: App Signals Enablement - E2E EKS Canary Testing
46
on:
57
schedule:

.github/workflows/appsignals-e2e-eks-test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ jobs:
159159
- name: Call endpoint and validate generated EMF logs
160160
id: log-validation
161161
if: steps.endpoint-check.outcome == 'success' && !cancelled()
162-
run: ./gradlew testing:validator:run --args='-c log-validation.yml
162+
run: ./gradlew testing:validator:run --args='-c eks/log-validation.yml
163163
--testing-id ${{ env.TESTING_ID }}
164164
--endpoint http://${{ env.APP_ENDPOINT }}
165165
--region ${{ env.AWS_DEFAULT_REGION }}
@@ -176,7 +176,7 @@ jobs:
176176
- name: Call endpoints and validate generated metrics
177177
id: metric-validation
178178
if: (success() || steps.log-validation.outcome == 'failure') && !cancelled()
179-
run: ./gradlew testing:validator:run --args='-c metric-validation.yml
179+
run: ./gradlew testing:validator:run --args='-c eks/metric-validation.yml
180180
--testing-id ${{ env.TESTING_ID }}
181181
--endpoint http://${{ env.APP_ENDPOINT }}
182182
--region ${{ env.AWS_DEFAULT_REGION }}
@@ -194,7 +194,7 @@ jobs:
194194
- name: Call endpoints and validate generated traces
195195
id: trace-validation
196196
if: (success() || steps.log-validation.outcome == 'failure' || steps.metric-validation.outcome == 'failure') && !cancelled()
197-
run: ./gradlew testing:validator:run --args='-c trace-validation.yml
197+
run: ./gradlew testing:validator:run --args='-c eks/trace-validation.yml
198198
--testing-id ${{ env.TESTING_ID }}
199199
--endpoint http://${{ env.APP_ENDPOINT }}
200200
--region ${{ env.AWS_DEFAULT_REGION }}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"agent": {
3+
"debug": true,
4+
"region": "$REGION"
5+
},
6+
"traces": {
7+
"traces_collected": {
8+
"app_signals": {
9+
"enabled": true
10+
}
11+
}
12+
},
13+
"logs": {
14+
"metrics_collected": {
15+
"app_signals": {
16+
"enabled": true
17+
}
18+
}
19+
}
20+
}

testing/terraform/ec2/main.tf

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
}
6+
}
7+
}
8+
9+
# Define the provider for AWS
10+
provider "aws" {}
11+
12+
resource "aws_default_vpc" "default" {}
13+
14+
resource "tls_private_key" "ssh_key" {
15+
algorithm = "RSA"
16+
rsa_bits = 4096
17+
}
18+
19+
resource "aws_key_pair" "aws_ssh_key" {
20+
key_name = "instance_key-${var.test_id}"
21+
public_key = tls_private_key.ssh_key.public_key_openssh
22+
}
23+
24+
locals {
25+
ssh_key_name = aws_key_pair.aws_ssh_key.key_name
26+
private_key_content = tls_private_key.ssh_key.private_key_pem
27+
}
28+
29+
resource "aws_instance" "main_service_instance" {
30+
ami = "ami-0b021814637c6d457" # Amazon Linux 2 (free tier)
31+
instance_type = "t2.micro"
32+
key_name = local.ssh_key_name
33+
iam_instance_profile = "APP_SIGNALS_EC2_TEST_ROLE"
34+
vpc_security_group_ids = [aws_default_vpc.default.default_security_group_id]
35+
associate_public_ip_address = true
36+
instance_initiated_shutdown_behavior = "terminate"
37+
38+
tags = {
39+
Name = "main-service-${var.test_id}"
40+
}
41+
}
42+
43+
resource "null_resource" "main_service_setup" {
44+
connection {
45+
type = "ssh"
46+
user = var.user
47+
private_key = local.private_key_content
48+
host = aws_instance.main_service_instance.public_ip
49+
}
50+
51+
provisioner "remote-exec" {
52+
inline = [
53+
# Install Java 11 and tmux
54+
"yes | sudo amazon-linux-extras install java-openjdk11",
55+
56+
# Copy in CW Agent configuration
57+
"agent_config='${replace(replace(file("./amazon-cloudwatch-agent.json"), "/\\s+/", ""), "$REGION", var.aws_region)}'",
58+
"echo $agent_config > amazon-cloudwatch-agent.json",
59+
60+
# Get and run CW agent rpm
61+
"wget -O cw-agent.rpm ${var.cw_agent_rpm}",
62+
"sudo rpm -U ./cw-agent.rpm",
63+
"sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:./amazon-cloudwatch-agent.json",
64+
65+
# Get ADOT
66+
"wget -O adot.jar ${var.adot_jar}",
67+
68+
# Get and run the sample application with configuration
69+
"aws s3 cp ${var.sample_app_jar} ./main-service.jar",
70+
71+
"JAVA_TOOL_OPTIONS=' -javaagent:/home/ec2-user/adot.jar' \\",
72+
"OTEL_METRICS_EXPORTER=none \\",
73+
"OTEL_SMP_ENABLED=true \\",
74+
"OTEL_AWS_SMP_EXPORTER_ENDPOINT=http://localhost:4315 \\",
75+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4315 \\",
76+
"OTEL_RESOURCE_ATTRIBUTES=aws.hostedin.environment=EC2,service.name=sample-application-${var.test_id} \\",
77+
"nohup java -jar main-service.jar &> nohup.out &",
78+
79+
# The application needs time to come up and reach a steady state, this should not take longer than 30 seconds
80+
"sleep 30"
81+
]
82+
}
83+
84+
depends_on = [aws_instance.main_service_instance]
85+
}
86+
87+
resource "aws_instance" "remote_service_instance" {
88+
ami = "ami-0b021814637c6d457" # Amazon Linux 2 (free tier)
89+
instance_type = "t2.micro"
90+
key_name = local.ssh_key_name
91+
iam_instance_profile = "APP_SIGNALS_EC2_TEST_ROLE"
92+
vpc_security_group_ids = [aws_default_vpc.default.default_security_group_id]
93+
associate_public_ip_address = true
94+
instance_initiated_shutdown_behavior = "terminate"
95+
96+
tags = {
97+
Name = "remote-service-${var.test_id}"
98+
}
99+
}
100+
101+
resource "null_resource" "remote_service_setup" {
102+
connection {
103+
type = "ssh"
104+
user = var.user
105+
private_key = local.private_key_content
106+
host = aws_instance.remote_service_instance.public_ip
107+
}
108+
109+
provisioner "remote-exec" {
110+
inline = [
111+
# Install Java 11 and tmux
112+
"yes | sudo amazon-linux-extras install java-openjdk11",
113+
114+
# Copy in CW Agent configuration
115+
"agent_config='${replace(replace(file("./amazon-cloudwatch-agent.json"), "/\\s+/", ""), "$REGION", var.aws_region)}'",
116+
"echo $agent_config > amazon-cloudwatch-agent.json",
117+
118+
# Get and run CW agent rpm
119+
"wget -O cw-agent.rpm ${var.cw_agent_rpm}",
120+
"sudo rpm -U ./cw-agent.rpm",
121+
"sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:./amazon-cloudwatch-agent.json",
122+
123+
# Get ADOT
124+
"wget -O adot.jar ${var.adot_jar}",
125+
126+
# Get and run the sample application with configuration
127+
"aws s3 cp ${var.sample_remote_app_jar} ./remote-service.jar",
128+
129+
"JAVA_TOOL_OPTIONS=' -javaagent:/home/ec2-user/adot.jar' \\",
130+
"OTEL_METRICS_EXPORTER=none \\",
131+
"OTEL_SMP_ENABLED=true \\",
132+
"OTEL_AWS_SMP_EXPORTER_ENDPOINT=http://localhost:4315 \\",
133+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4315 \\",
134+
"OTEL_RESOURCE_ATTRIBUTES=aws.hostedin.environment=EC2,service.name=sample-remote-application-${var.test_id} \\",
135+
"nohup java -jar remote-service.jar &> nohup.out &",
136+
137+
# The application needs time to come up and reach a steady state, this should not take longer than 30 seconds
138+
"sleep 30"
139+
]
140+
}
141+
142+
depends_on = [aws_instance.remote_service_instance]
143+
}

0 commit comments

Comments
 (0)