From 7fbc726a33ede99528b292a0728f15cb444a7138 Mon Sep 17 00:00:00 2001 From: harryryu Date: Fri, 26 Jul 2024 15:45:28 -0700 Subject: [PATCH] Deploy Traffic Generator --- .../traffic-generator-image-build.yml | 100 ++++++++++++++++++ traffic-generator/Dockerfile | 21 ++++ traffic-generator/index.js | 73 +++++++++++++ traffic-generator/package.json | 12 +++ 4 files changed, 206 insertions(+) create mode 100644 .github/workflows/traffic-generator-image-build.yml create mode 100644 traffic-generator/Dockerfile create mode 100644 traffic-generator/index.js create mode 100644 traffic-generator/package.json diff --git a/.github/workflows/traffic-generator-image-build.yml b/.github/workflows/traffic-generator-image-build.yml new file mode 100644 index 000000000..5bf04d45a --- /dev/null +++ b/.github/workflows/traffic-generator-image-build.yml @@ -0,0 +1,100 @@ +# This workflow will build and the traffic generator image 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 +name: Create and Push Traffic Generator Image + +on: + workflow_dispatch: + push: +# branches: +# - main +# paths: +# - '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: 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: + 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: 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 \ No newline at end of file diff --git a/traffic-generator/Dockerfile b/traffic-generator/Dockerfile new file mode 100644 index 000000000..e20787412 --- /dev/null +++ b/traffic-generator/Dockerfile @@ -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" ] diff --git a/traffic-generator/index.js b/traffic-generator/index.js new file mode 100644 index 000000000..5cc2c7bd4 --- /dev/null +++ b/traffic-generator/index.js @@ -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); \ No newline at end of file diff --git a/traffic-generator/package.json b/traffic-generator/package.json new file mode 100644 index 000000000..d1ccdec12 --- /dev/null +++ b/traffic-generator/package.json @@ -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" + } +} \ No newline at end of file