Skip to content

Commit 8eac575

Browse files
committed
Run performance tests on AWS ECS
* Build custom jmeter docker image * Push image to AWS container registry * Performance workflow starts on-demand ECS task using that image with AWS CLI * URN and all other environment variables will be set as environment variables for the task
1 parent 59fc393 commit 8eac575

File tree

3 files changed

+292
-153
lines changed

3 files changed

+292
-153
lines changed

.github/workflows/performance.yaml

Lines changed: 108 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
name: Performance
2+
run-name: Test ${{ inputs.testToRun }} against ${{ inputs.URN }}
23

34
on:
45
push:
@@ -29,16 +30,23 @@ on:
2930
duration:
3031
description: "Optional (default 3600) Duration of nurse journey test, in seconds. This will include ramp-up."
3132
required: false
32-
type: string
33-
default: '3600'
33+
type: number
34+
default: 3600
3435
threads:
3536
description: "Optional (default 70) Threads to run. Equivalent to the number of nurses using the system."
3637
required: false
37-
type: string
38-
default: '70'
38+
type: number
39+
default: 70
3940
ramp_up:
4041
description: "Optional (default 900) Ramp-up time in seconds. Threads will be gradually started up over this time."
4142
required: false
43+
type: number
44+
default: 900
45+
vaccination_loop:
46+
description: "Optional (default 20) Vaccination loop (nurse journey only). The number of vaccinations each nurse will perform before logging and back in again."
47+
required: false
48+
type: number
49+
default: 20
4250
type: string
4351
default: '900'
4452
row_count:
@@ -58,164 +66,111 @@ on:
5866
default: 'qa.mavistesting.com'
5967

6068
jobs:
61-
nurse_journey_performance_test:
69+
check-image-presence:
70+
name: Check if docker image already exists
6271
runs-on: ubuntu-latest
63-
64-
if: ${{ github.event_name == 'workflow_dispatch' }}
65-
66-
env:
67-
jmeter_version: 5.6.3
68-
cmdrunner_version: 2.3
69-
jmeter_plugins_manager_version: 1.7.0
70-
jmeter_plugins: jpgc-udp,jpgc-graphs-basic,jpgc-dummy,bzm-random-csv,jpgc-sts
71-
# jmeter_home: ${{ github.workspace }}/jmeter-${{ env.jmeter_version }}
72-
72+
permissions:
73+
id-token: write
74+
outputs:
75+
build-needed: ${{ steps.check-image.outputs.build-needed }}
7376
steps:
74-
- uses: actions/checkout@v5
75-
76-
- name: Cache jmeter
77-
id: cache-jmeter
78-
uses: actions/cache@v4
79-
env:
80-
cache-name: cache-jmeter
77+
- name: Configure AWS Credentials
78+
uses: aws-actions/configure-aws-credentials@v5
8179
with:
82-
path: jmeter
83-
key: jmeter
84-
85-
#Having run each step separately, it's only the curl request that takes time. So including the unzip and move means I can cache the 'jmeter' folder entirely
86-
#STS also requires some files in the jmeter folder, but I don't want those cached so the 'touch' commands are being included in the plugins step
87-
#Finally move some of the report generator options into the jmeter folder to simplify the jmeter command in each execution step
88-
89-
- name: Install JMeter
90-
if: ${{ steps.cache-jmeter.outputs.cache-hit != 'true'}}
91-
run: |
92-
curl -sSO https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${{ env.jmeter_version }}.tgz
93-
tar xzf apache-jmeter-${{ env.jmeter_version }}.tgz
94-
mv apache-jmeter-${{ env.jmeter_version }} jmeter
95-
curl -sSO --output-dir jmeter/lib https://repo1.maven.org/maven2/kg/apc/cmdrunner/${{ env.cmdrunner_version }}/cmdrunner-${{ env.cmdrunner_version }}.jar
96-
curl -LsS --output jmeter/lib/ext/jmeter-plugins-manager-${{ env.jmeter_plugins_manager_version }}.jar https://jmeter-plugins.org/get/
97-
java -cp jmeter/lib/ext/jmeter-plugins-manager-${{ env.jmeter_plugins_manager_version }}.jar org.jmeterplugins.repository.PluginManagerCMDInstaller
98-
chmod +x jmeter/bin/PluginsManagerCMD.sh
99-
100-
- name: Install JMeter plugins
101-
if: env.jmeter_plugins != ''
80+
role-to-assume: arn:aws:iam::393416225559:role/GitHubPerformancetestRole
81+
aws-region: eu-west-2
82+
- name: Check if image exists
83+
id: check-image
10284
run: |
103-
if [ -x "$(command -v parallel)" ]; then
104-
echo "Using GNU parallel to install plugins"
105-
parallel -d, -j 5 -n 1 jmeter/bin/PluginsManagerCMD.sh install {} ::: ${{ env.jmeter_plugins }}
85+
if aws ecr describe-images --repository-name performancetest --image-ids imageTag=${{ github.sha }} > /dev/null 2>&1; then
86+
echo "Docker image with given tag already exists"
10687
else
107-
echo "GNU parallel not found, installing plugins sequentially"
108-
IFS=',' read -ra PLUGINS <<< "${{ env.jmeter_plugins }}"
109-
for plugin in "${PLUGINS[@]}"; do
110-
jmeter/bin/PluginsManagerCMD.sh install $plugin
111-
done
88+
echo "Docker image does not exist. Build needed"
89+
echo "build-needed=true" >> $GITHUB_OUTPUT
11290
fi
113-
curl -sSO --output-dir jmeter/lib https://repo1.maven.org/maven2/org/apache/tika/tika-app/1.28.5/tika-app-1.28.5.jar
114-
sed -i '/<Logger name="org.apache.jmeter.junit" level="debug" \/>/a \ <Logger name="org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase" level="info" additivity="false"\/>' jmeter/bin/log4j2.xml
115-
sed -i '$ajmeterPlugin.sts.loadAndRunOnStartup=true \njmeterPlugin.sts.port=9191 \njmeterPlugin.sts.daemon=false \njsr223.init.file=jmeter/bin/simple-table-server.groovy' jmeter/bin/user.properties
116-
sed -i '$ajmeter.reportgenerator.report_title="MAVIS test report" \njmeter.reportgenerator.overall_granularity=10000 \njmeter.reportgenerator.sample_filter="^.*[^0-9]$"' jmeter/bin/user.properties
117-
touch jmeter/consents.txt
118-
touch jmeter/vaccinations.txt
119-
120-
- name: Set timestamp
121-
id: timestamp
122-
run: echo "timestamp=$(date '+%Y%m%d%H%M%S')" >> $GITHUB_ENV
123-
124-
- name: Create new cohort file
125-
if: inputs.runImport == true
126-
run: |
127-
mkdir -p generate-cohort-output
128-
generate_cohort_output_dir=generate-cohort-output
129-
jmeter/bin/jmeter -n -t performance-tests/STS/generate-cohort.jmx \
130-
-l $generate_cohort_output_dir/samples.jtl \
131-
-j $generate_cohort_output_dir/jmeter.log \
132-
-JAuthToken=${{secrets.HTTP_AUTH_TOKEN_FOR_TESTS}} \
133-
-JURN=${{inputs.URN}} \
134-
-JBaseURL=${{inputs.BaseURL}} \
135-
-JRowCount=${{inputs.row_count}}
136-
137-
- name: Run file import for new file
138-
if: inputs.runImport == true
139-
run: |
140-
mkdir -p import-output
141-
import_output_dir=import-output
142-
jmeter/bin/jmeter -n -t performance-tests/STS/upload-cohort-data.jmx \
143-
-l $import_output_dir/samples.jtl \
144-
-j $import_output_dir/jmeter.log \
145-
-JAuthToken=${{secrets.HTTP_AUTH_TOKEN_FOR_TESTS}} \
146-
-JUser=${{inputs.user}} \
147-
-JBaseURL=${{inputs.BaseURL}} \
148-
-JInputFile="cohortnew.csv"
149-
150-
- name: Run Consent Journey
151-
if: inputs.runConsent == true
152-
run: |
153-
mkdir -p consent-output
154-
consent_output_dir=consent-output
155-
jmeter/bin/jmeter -n -t performance-tests/STS/consent-journey.jmx \
156-
-l $consent_output_dir/samples.jtl \
157-
-j $consent_output_dir/jmeter.log \
158-
-e -o $consent_output_dir/consent-report \
159-
-Jjmeter.reportgenerator.report_title="MAVIS test report" \
160-
-Jjmeter.reportgenerator.overall_granularity=10000 \
161-
-Jjmeter.reportgenerator.sample_filter="^.*[^0-9]$" \
162-
-JAuthToken=${{secrets.HTTP_AUTH_TOKEN_FOR_TESTS}} \
163-
-JLoops=-1 \
164-
-JThreads=5 \
165-
-JRampUp=60 \
166-
-JDuration=${{inputs.duration}} \
167-
-JUser=${{inputs.user}} \
168-
-JBaseURL=${{inputs.BaseURL}} \
169-
-JURN=${{inputs.URN}}
170-
171-
- name: Upload consent journey JMeter output
172-
if: inputs.runConsent == true
173-
uses: actions/upload-artifact@v5
91+
build-and-push:
92+
needs: check-image-presence
93+
if: needs.check-image-presence.outputs.build-needed == 'true'
94+
runs-on: ubuntu-latest
95+
permissions:
96+
id-token: write
97+
steps:
98+
- name: Checkout code
99+
uses: actions/checkout@v5
100+
- name: Configure AWS Credentials
101+
uses: aws-actions/configure-aws-credentials@v5
174102
with:
175-
name: jmeter-consent-journey-output-${{ env.timestamp }}
176-
path: consent-output
177-
if-no-files-found: warn
178-
179-
- name: Run nurse Journey
180-
if: inputs.runNurse == true
103+
role-to-assume: arn:aws:iam::393416225559:role/GitHubPerformancetestRole
104+
aws-region: eu-west-2
105+
- name: Login to ECR
106+
id: login-ecr
107+
uses: aws-actions/amazon-ecr-login@v2
108+
- name: Build and push performancetest docker image
181109
run: |
182-
mkdir -p nurse-output
183-
nurse_output_dir=nurse-output
184-
jmeter/bin/jmeter -n -t performance-tests/STS/nurse-journey.jmx \
185-
-l $nurse_output_dir/samples.jtl \
186-
-j $nurse_output_dir/jmeter.log \
187-
-e -o $nurse_output_dir/report \
188-
-Jjmeter.reportgenerator.report_title="MAVIS test report" \
189-
-Jjmeter.reportgenerator.overall_granularity=10000 \
190-
-Jjmeter.reportgenerator.sample_filter="^.*[^0-9]$" \
191-
-JAuthToken=${{secrets.HTTP_AUTH_TOKEN_FOR_TESTS}} \
192-
-JLoops=-1 \
193-
-JThreads=${{inputs.threads}} \
194-
-JRampUp=${{inputs.ramp_up}} \
195-
-JDuration=${{inputs.duration}} \
196-
-JUser=${{inputs.user}} \
197-
-JBaseURL=${{inputs.BaseURL}} \
198-
-JURN=${{inputs.URN}}
199-
200-
- name: Upload nurse journey JMeter output
201-
if: inputs.runNurse == true
202-
uses: actions/upload-artifact@v5
110+
cd performance-tests
111+
docker build -t "393416225559.dkr.ecr.eu-west-2.amazonaws.com/performancetest:${{ github.sha }}" .
112+
docker push "393416225559.dkr.ecr.eu-west-2.amazonaws.com/performancetest:${{ github.sha }}"
113+
run-performance-test:
114+
needs: [build-and-push, check-image-presence]
115+
if: ${{ !cancelled() &&
116+
(needs.build-and-push.result == 'success' || needs.check-image-presence.result == 'success') }}
117+
runs-on: ubuntu-latest
118+
permissions:
119+
id-token: write
120+
timeout-minutes: 180
121+
steps:
122+
- uses: actions/checkout@v5
123+
- name: Configure AWS credentials
124+
uses: aws-actions/configure-aws-credentials@v5
203125
with:
204-
name: jmeter-nurse-journey-output-${{ env.timestamp }}
205-
path: nurse-output
206-
if-no-files-found: warn
207-
208-
- name: Publish report to GH Pages
209-
if: inputs.runNurse == true
210-
uses: peaceiris/actions-gh-pages@v4
126+
role-to-assume: arn:aws:iam::393416225559:role/GitHubPerformancetestRole
127+
aws-region: eu-west-2
128+
- name: Create task definition
129+
id: create-task-definition
130+
uses: aws-actions/amazon-ecs-render-task-definition@v1
211131
with:
212-
github_token: ${{ secrets.GITHUB_TOKEN }}
213-
publish_branch: gh-pages
214-
publish_dir: nurse-output/report
215-
destination_dir: JMeter/report-${{ env.timestamp }}
216-
keep_files: true
217-
218-
- name: Set Job Summary
132+
task-definition-family: "performancetest-task-definition-template"
133+
container-name: "performancetest-container"
134+
image: "393416225559.dkr.ecr.eu-west-2.amazonaws.com/performancetest:${{ github.sha }}"
135+
environment-variables: |
136+
TEST_TO_RUN=${{ inputs.testToRun }}
137+
URN=${{ inputs.URN }}
138+
DURATION=${{ inputs.duration }}
139+
THREADS=${{ inputs.threads }}
140+
RAMP_UP=${{ inputs.ramp_up }}
141+
VACCINATION_LOOP=${{ inputs.vaccination_loop }}
142+
ROW_COUNT=${{ inputs.row_count }}
143+
secrets: |
144+
AUTH_TOKEN=arn:aws:secretsmanager:eu-west-2:393416225559:secret:performancetest/auth-token
145+
- name: Register task definition
146+
id: register-task-definition
147+
run: |
148+
file_path="performancetest-task-definition.json"
149+
family_name="performancetest-task-definition"
150+
echo "$(jq --arg f "$family_name" '.family = $f' "${{ steps.create-task-definition.outputs.task-definition }}")" > "$file_path"
151+
task_definition_arn=$(aws ecs register-task-definition \
152+
--cli-input-json file://$file_path \
153+
--query 'taskDefinition.taskDefinitionArn' \
154+
--output text
155+
)
156+
echo "task_definition_arn=$task_definition_arn" >> $GITHUB_OUTPUT
157+
- name: Run ECS Task
158+
id: run-task
219159
run: |
220-
echo "Test report URL is https://nhsdigital.github.io/manage-vaccinations-in-schools-testing/JMeter/report-${{ env.timestamp }}/" >> $GITHUB_STEP_SUMMARY
160+
echo "Starting ECS task for ${{ inputs.testToRun }} test against URN: ${{ inputs.URN }}"
161+
162+
# Prepare network configuration
163+
subnet_id=$(aws ec2 describe-subnets --filters Name=tag:Name,Values=performancetest-subnet --query 'Subnets[0].SubnetId' --output text)
164+
security_group_id=$(aws ec2 describe-security-groups --filters Name=group-name,Values=performancetest-sg --query 'SecurityGroups[0].GroupId' --output text)
221165
166+
aws ecs run-task \
167+
--cluster performancetest \
168+
--task-definition ${{ steps.register-task-definition.outputs.task_definition_arn }} \
169+
--launch-type FARGATE \
170+
--network-configuration "awsvpcConfiguration={subnets=[$subnet_id],securityGroups=[$security_group_id],assignPublicIp=ENABLED}" \
171+
--overrides '{
172+
"containerOverrides": [{
173+
"name": "performancetest-container"
174+
}]
175+
}'
176+

performance-tests/Dockerfile

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
FROM eclipse-temurin:25-jre-jammy
2+
3+
ENV JMETER_VERSION=5.6.3 \
4+
CMDRUNNER_VERSION=2.3 \
5+
JMETER_PLUGINS_MANAGER_VERSION=1.7.0 \
6+
JMETER_PLUGINS=jpgc-udp,jpgc-graphs-basic,jpgc-dummy,bzm-random-csv \
7+
JMETER_HOME=/opt/jmeter \
8+
PATH="/opt/jmeter/bin:${PATH}"
9+
10+
RUN apt-get update && apt-get install -y curl tar parallel && rm -rf /var/lib/apt/lists/*
11+
12+
WORKDIR /opt
13+
14+
# Install JMeter
15+
RUN curl -sSO https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz \
16+
&& tar xzf apache-jmeter-${JMETER_VERSION}.tgz \
17+
&& mv apache-jmeter-${JMETER_VERSION} jmeter \
18+
&& rm apache-jmeter-${JMETER_VERSION}.tgz
19+
20+
RUN curl -sSO --output-dir ${JMETER_HOME}/lib \
21+
https://repo1.maven.org/maven2/kg/apc/cmdrunner/${CMDRUNNER_VERSION}/cmdrunner-${CMDRUNNER_VERSION}.jar
22+
23+
RUN curl -LsS --output ${JMETER_HOME}/lib/ext/jmeter-plugins-manager-${JMETER_PLUGINS_MANAGER_VERSION}.jar \
24+
https://jmeter-plugins.org/get/ \
25+
&& java -cp ${JMETER_HOME}/lib/ext/jmeter-plugins-manager-${JMETER_PLUGINS_MANAGER_VERSION}.jar \
26+
org.jmeterplugins.repository.PluginManagerCMDInstaller \
27+
&& chmod +x ${JMETER_HOME}/bin/PluginsManagerCMD.sh
28+
29+
RUN sed -i '/<Logger name="org.apache.jmeter.junit" level="debug" \/>/a \ <Logger name="org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase" level="info" additivity="false"\/>' \
30+
${JMETER_HOME}/bin/log4j2.xml
31+
32+
# Install JMeter plugins
33+
RUN echo "${JMETER_PLUGINS}" | tr ',' '\n' | \
34+
parallel -j 5 "${JMETER_HOME}/bin/PluginsManagerCMD.sh install {}"
35+
36+
# Create directories for test files and outputs
37+
RUN mkdir -p /performance-tests /output
38+
WORKDIR /performance-tests
39+
COPY E2E/ E2E
40+
COPY entrypoint.sh /entrypoint.sh
41+
RUN chmod +x /entrypoint.sh
42+
43+
ENTRYPOINT ["/entrypoint.sh"]
44+
45+
# Mount directories as volumes to make them writeable
46+
VOLUME [ "/output" ]

0 commit comments

Comments
 (0)