Skip to content

Commit de02191

Browse files
authored
Deploy Traffic Generator (#140)
*Issue description:* This is a part of four series of PR to remove public endpoints for E2E testing to comply with security best practices. - [Deploy Traffic Generator](#140) - [Remove Public Endpoints from K8s](#141) - [Remove Public Endpoints from EKS](#139) - [Remove Public Endpoints from EC2](#144) Since the public endpoints will be removed, we are unable to call the sample app APIs directly from the workflow. Therefore, we will be using a traffic generator that is installed alongside the sample app applications to call the APIs This PR will be responsible for creating the traffic generator as well as saving them to private ECR and S3 bucket. The private ECR will be used by EKS and K8s platform while the S3 bucket will be used by EC2 Platforms. The traffic generator will first wait while it receives the appropriate environment variable. Once that is received, it will ping the API endpoints every minute. There is some additional logic depending on which canary it is running for. Test run: https://github.com/aws-observability/aws-application-signals-test-framework/actions/runs/10118562925 Test run after comment: https://github.com/aws-observability/aws-application-signals-test-framework/actions/runs/10148623963 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 1308ba3 commit de02191

File tree

4 files changed

+206
-0
lines changed

4 files changed

+206
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# This workflow will build and push the traffic generator to each region whenever there is an update made to the traffic-generator folder.
2+
# This image will be used by EKS and K8s test to call sample app endpoints while the zip files will be used by EC2 Platforms
3+
name: Create and Push Traffic Generator
4+
5+
on:
6+
workflow_dispatch:
7+
push:
8+
branches:
9+
- main
10+
paths:
11+
- 'sample-apps/traffic-generator/**'
12+
13+
permissions:
14+
id-token: write
15+
contents: read
16+
17+
env:
18+
E2E_TEST_ACCOUNT_ID: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ACCOUNT_ID }}
19+
E2E_TEST_ROLE_NAME: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ROLE_NAME }}
20+
21+
jobs:
22+
build-and-push-image:
23+
runs-on: ubuntu-latest
24+
strategy:
25+
matrix:
26+
aws-region: ['af-south-1','ap-east-1','ap-northeast-1','ap-northeast-2','ap-northeast-3','ap-south-1','ap-south-2','ap-southeast-1',
27+
'ap-southeast-2','ap-southeast-3','ap-southeast-4','ca-central-1','eu-central-1','eu-central-2','eu-north-1',
28+
'eu-south-1','eu-south-2','eu-west-1','eu-west-2','eu-west-3','il-central-1','me-central-1','me-south-1', 'sa-east-1',
29+
'us-east-1','us-east-2', 'us-west-1', 'us-west-2']
30+
steps:
31+
- name: Checkout repository
32+
uses: actions/checkout@v4
33+
34+
- name: Configure AWS Credentials
35+
uses: aws-actions/configure-aws-credentials@v4
36+
with:
37+
role-to-assume: arn:aws:iam::${{ env.E2E_TEST_ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
38+
aws-region: us-east-1
39+
40+
- name: Retrieve account
41+
uses: aws-actions/aws-secretsmanager-get-secrets@v1
42+
with:
43+
secret-ids: |
44+
ACCOUNT_ID, region-account/${{ matrix.aws-region }}
45+
46+
- name: Configure AWS Credentials
47+
uses: aws-actions/configure-aws-credentials@v4
48+
with:
49+
role-to-assume: arn:aws:iam::${{ env.ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
50+
aws-region: ${{ matrix.aws-region }}
51+
52+
- name: Login to Amazon ECR
53+
id: login-ecr
54+
uses: aws-actions/amazon-ecr-login@v2
55+
56+
- name: Build, tag, and push image to Amazon ECR
57+
working-directory: sample-apps/traffic-generator
58+
env:
59+
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
60+
REPOSITORY: e2e-test-resource
61+
IMAGE_TAG: traffic-generator
62+
run: |
63+
docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
64+
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
65+
66+
upload-files-to-s3:
67+
runs-on: ubuntu-latest
68+
strategy:
69+
matrix:
70+
aws-region: ['af-south-1','ap-east-1','ap-northeast-1','ap-northeast-2','ap-northeast-3','ap-south-1','ap-south-2','ap-southeast-1',
71+
'ap-southeast-2','ap-southeast-3','ap-southeast-4','ca-central-1','eu-central-1','eu-central-2','eu-north-1',
72+
'eu-south-1','eu-south-2','eu-west-1','eu-west-2','eu-west-3','il-central-1','me-central-1','me-south-1', 'sa-east-1',
73+
'us-east-1','us-east-2', 'us-west-1', 'us-west-2']
74+
steps:
75+
- name: Checkout repository
76+
uses: actions/checkout@v4
77+
78+
- name: Configure AWS Credentials
79+
uses: aws-actions/configure-aws-credentials@v4
80+
with:
81+
role-to-assume: arn:aws:iam::${{ env.E2E_TEST_ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
82+
aws-region: us-east-1
83+
84+
- name: Retrieve account
85+
uses: aws-actions/aws-secretsmanager-get-secrets@v1
86+
with:
87+
secret-ids: |
88+
ACCOUNT_ID, region-account/${{ matrix.aws-region }}
89+
90+
- name: Configure AWS Credentials
91+
uses: aws-actions/configure-aws-credentials@v4
92+
with:
93+
role-to-assume: arn:aws:iam::${{ env.ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
94+
aws-region: ${{ matrix.aws-region }}
95+
96+
- name: Upload traffic generator files
97+
working-directory: sample-apps/traffic-generator
98+
run: |
99+
zip traffic-generator.zip ./index.js ./package.json
100+
aws s3 cp traffic-generator.zip s3://aws-appsignals-sample-app-prod-${{ matrix.aws-region }}/traffic-generator.zip
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Use the official lightweight Node.js 16 image.
2+
# https://hub.docker.com/_/node
3+
# FROM node:16-slim
4+
FROM public.ecr.aws/eks-distro-build-tooling/nodejs:16
5+
6+
# Create and change to the app directory
7+
WORKDIR /usr/src/app
8+
9+
# Copy application dependency manifests to the container image.
10+
# A wildcard is used to ensure copying both package.json AND package-lock.json (if available).
11+
# Copying this first prevents re-running npm install on every code change.
12+
COPY package*.json ./
13+
14+
# Install dependencies
15+
RUN npm install
16+
17+
# Copy local code to the container image.
18+
COPY . .
19+
20+
# Run the web service on container startup.
21+
CMD [ "npm", "start" ]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
const axios = require('axios');
2+
3+
// Send API requests to the sample app
4+
const sendRequests = async (urls) => {
5+
try {
6+
const fetchPromises = urls.map(url => axios.get(url));
7+
const responses = await Promise.all(fetchPromises);
8+
9+
// Handle the responses
10+
responses.forEach((response, index) => {
11+
if (response.status === 200) {
12+
const data = response.data;
13+
console.log(`Response from ${urls[index]}:`, data);
14+
} else {
15+
console.error(`Failed to fetch ${urls[index]}:`, response.statusText);
16+
}
17+
});
18+
} catch (error) {
19+
console.error('Error sending GET requests:', error);
20+
}
21+
}
22+
23+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
24+
25+
// This loop will run until the environment variables are available
26+
const waitForEnvVariables = async () => {
27+
while (!process.env.MAIN_ENDPOINT || !process.env.REMOTE_ENDPOINT || !process.env.ID || !process.env.CANARY_TYPE) {
28+
console.log('Environment variables not set. Waiting for 10 seconds...');
29+
await sleep(10000); // Wait for 10 seconds
30+
}
31+
};
32+
33+
// Traffic generator that sends traffic every specified interval. Send request immediately then every 2 minutes afterwords
34+
const trafficGenerator = async (interval) => {
35+
await waitForEnvVariables();
36+
37+
const mainEndpoint = process.env.MAIN_ENDPOINT;
38+
const remoteEndpoint = process.env.REMOTE_ENDPOINT;
39+
const id = process.env.ID;
40+
const canaryType = process.env.CANARY_TYPE
41+
42+
let urls = [
43+
`http://${mainEndpoint}/outgoing-http-call`,
44+
`http://${mainEndpoint}/aws-sdk-call?ip=${remoteEndpoint}&testingId=${id}`,
45+
`http://${mainEndpoint}/remote-service?ip=${remoteEndpoint}&testingId=${id}`,
46+
`http://${mainEndpoint}/client-call`
47+
];
48+
49+
if (canaryType === 'java-eks' || canaryType === 'python-eks') {
50+
urls.push(`http://${mainEndpoint}/mysql`)
51+
}
52+
53+
// Need to call some APIs so that it exceeds the metric limiter threshold and make the test
54+
// APIs generate AllOtherOperations metric. Sleep for a minute to let cloudwatch service process the API call
55+
// Calling it here before calling the remote sample app endpoint because the API generated by it is validated
56+
// for AllOtherRemoteOperations in the metric validation step
57+
if (canaryType === 'java-metric-limiter'){
58+
const fakeUrls = [
59+
`http://${mainEndpoint}`,
60+
`http://${mainEndpoint}/fake-endpoint`
61+
]
62+
// Send the fake requests and wait a minute
63+
await sendRequests(fakeUrls);
64+
await sleep(60000);
65+
}
66+
67+
await sendRequests(urls);
68+
setInterval(() => sendRequests(urls), interval);
69+
}
70+
71+
const interval = 60 * 1000;
72+
// Start sending GET requests every minute (60,000 milliseconds)
73+
trafficGenerator(interval);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "traffic-generator",
3+
"version": "1.0.0",
4+
"description": "A simple traffic generator that sends GET requests to a list of URLs every 2 minutes",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "node index.js"
8+
},
9+
"dependencies": {
10+
"axios": "^1.4.0"
11+
}
12+
}

0 commit comments

Comments
 (0)