Skip to content

Commit

Permalink
Deploy Traffic Generator (#140)
Browse files Browse the repository at this point in the history
*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.
  • Loading branch information
harrryr authored Jul 29, 2024
1 parent 1308ba3 commit de02191
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
100 changes: 100 additions & 0 deletions .github/workflows/traffic-generator-image-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# This workflow will build and push the traffic generator to each region whenever there is an update made to the traffic-generator folder.
# 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
name: Create and Push Traffic Generator

on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'sample-apps/traffic-generator/**'

permissions:
id-token: write
contents: read

env:
E2E_TEST_ACCOUNT_ID: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ACCOUNT_ID }}
E2E_TEST_ROLE_NAME: ${{ secrets.APPLICATION_SIGNALS_E2E_TEST_ROLE_NAME }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
strategy:
matrix:
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',
'ap-southeast-2','ap-southeast-3','ap-southeast-4','ca-central-1','eu-central-1','eu-central-2','eu-north-1',
'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',
'us-east-1','us-east-2', 'us-west-1', 'us-west-2']
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ env.E2E_TEST_ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
aws-region: us-east-1

- name: Retrieve account
uses: aws-actions/aws-secretsmanager-get-secrets@v1
with:
secret-ids: |
ACCOUNT_ID, region-account/${{ matrix.aws-region }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ env.ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
aws-region: ${{ matrix.aws-region }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2

- name: Build, tag, and push image to Amazon ECR
working-directory: sample-apps/traffic-generator
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: e2e-test-resource
IMAGE_TAG: traffic-generator
run: |
docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
upload-files-to-s3:
runs-on: ubuntu-latest
strategy:
matrix:
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',
'ap-southeast-2','ap-southeast-3','ap-southeast-4','ca-central-1','eu-central-1','eu-central-2','eu-north-1',
'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',
'us-east-1','us-east-2', 'us-west-1', 'us-west-2']
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ env.E2E_TEST_ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
aws-region: us-east-1

- name: Retrieve account
uses: aws-actions/aws-secretsmanager-get-secrets@v1
with:
secret-ids: |
ACCOUNT_ID, region-account/${{ matrix.aws-region }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ env.ACCOUNT_ID }}:role/${{ env.E2E_TEST_ROLE_NAME }}
aws-region: ${{ matrix.aws-region }}

- name: Upload traffic generator files
working-directory: sample-apps/traffic-generator
run: |
zip traffic-generator.zip ./index.js ./package.json
aws s3 cp traffic-generator.zip s3://aws-appsignals-sample-app-prod-${{ matrix.aws-region }}/traffic-generator.zip
21 changes: 21 additions & 0 deletions sample-apps/traffic-generator/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Use the official lightweight Node.js 16 image.
# https://hub.docker.com/_/node
# FROM node:16-slim
FROM public.ecr.aws/eks-distro-build-tooling/nodejs:16

# Create and change to the app directory
WORKDIR /usr/src/app

# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (if available).
# Copying this first prevents re-running npm install on every code change.
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy local code to the container image.
COPY . .

# Run the web service on container startup.
CMD [ "npm", "start" ]
73 changes: 73 additions & 0 deletions sample-apps/traffic-generator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const axios = require('axios');

// Send API requests to the sample app
const sendRequests = async (urls) => {
try {
const fetchPromises = urls.map(url => axios.get(url));
const responses = await Promise.all(fetchPromises);

// Handle the responses
responses.forEach((response, index) => {
if (response.status === 200) {
const data = response.data;
console.log(`Response from ${urls[index]}:`, data);
} else {
console.error(`Failed to fetch ${urls[index]}:`, response.statusText);
}
});
} catch (error) {
console.error('Error sending GET requests:', error);
}
}

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

// This loop will run until the environment variables are available
const waitForEnvVariables = async () => {
while (!process.env.MAIN_ENDPOINT || !process.env.REMOTE_ENDPOINT || !process.env.ID || !process.env.CANARY_TYPE) {
console.log('Environment variables not set. Waiting for 10 seconds...');
await sleep(10000); // Wait for 10 seconds
}
};

// Traffic generator that sends traffic every specified interval. Send request immediately then every 2 minutes afterwords
const trafficGenerator = async (interval) => {
await waitForEnvVariables();

const mainEndpoint = process.env.MAIN_ENDPOINT;
const remoteEndpoint = process.env.REMOTE_ENDPOINT;
const id = process.env.ID;
const canaryType = process.env.CANARY_TYPE

let urls = [
`http://${mainEndpoint}/outgoing-http-call`,
`http://${mainEndpoint}/aws-sdk-call?ip=${remoteEndpoint}&testingId=${id}`,
`http://${mainEndpoint}/remote-service?ip=${remoteEndpoint}&testingId=${id}`,
`http://${mainEndpoint}/client-call`
];

if (canaryType === 'java-eks' || canaryType === 'python-eks') {
urls.push(`http://${mainEndpoint}/mysql`)
}

// Need to call some APIs so that it exceeds the metric limiter threshold and make the test
// APIs generate AllOtherOperations metric. Sleep for a minute to let cloudwatch service process the API call
// Calling it here before calling the remote sample app endpoint because the API generated by it is validated
// for AllOtherRemoteOperations in the metric validation step
if (canaryType === 'java-metric-limiter'){
const fakeUrls = [
`http://${mainEndpoint}`,
`http://${mainEndpoint}/fake-endpoint`
]
// Send the fake requests and wait a minute
await sendRequests(fakeUrls);
await sleep(60000);
}

await sendRequests(urls);
setInterval(() => sendRequests(urls), interval);
}

const interval = 60 * 1000;
// Start sending GET requests every minute (60,000 milliseconds)
trafficGenerator(interval);
12 changes: 12 additions & 0 deletions sample-apps/traffic-generator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "traffic-generator",
"version": "1.0.0",
"description": "A simple traffic generator that sends GET requests to a list of URLs every 2 minutes",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"axios": "^1.4.0"
}
}

0 comments on commit de02191

Please sign in to comment.