Performance test against performance.mavistesting.com #241
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Performance | |
| run-name: Performance test against ${{ inputs.BaseURL }} | |
| on: | |
| push: | |
| branches: | |
| - performance-testing | |
| workflow_dispatch: | |
| inputs: | |
| AddNewSession: | |
| description: "Add a new session date (not needed if there are already enough patients)" | |
| required: true | |
| type: boolean | |
| default: true | |
| runConsent: | |
| description: "Run consent journey" | |
| required: true | |
| type: boolean | |
| default: true | |
| runNurse: | |
| description: "Run the nurse journey" | |
| required: true | |
| type: boolean | |
| default: true | |
| duration: | |
| description: "Optional (default 3600) Duration of nurse journey test, in seconds. This will include ramp-up." | |
| required: false | |
| type: number | |
| default: 3600 | |
| threads: | |
| description: "Optional (default 70) Threads to run. Equivalent to the number of nurses using the system." | |
| required: false | |
| type: number | |
| default: 70 | |
| ramp_up: | |
| description: "Optional (default 900) Ramp-up time in seconds. Threads will be gradually started up over this time." | |
| required: false | |
| type: number | |
| default: 900 | |
| user: | |
| description: "Optional (default Nurse perftest) user." | |
| required: true | |
| type: string | |
| default: '[email protected]' | |
| BaseURL: | |
| description: "Optional (default performance.mavistesting.com) URL" | |
| required: true | |
| type: string | |
| default: 'performance.mavistesting.com' | |
| jobs: | |
| check-image-presence: | |
| name: Check if docker image already exists | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| outputs: | |
| build-needed: ${{ steps.check-image.outputs.build-needed }} | |
| steps: | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@v5 | |
| with: | |
| role-to-assume: arn:aws:iam::393416225559:role/GitHubAssuranceTestRole | |
| aws-region: eu-west-2 | |
| - name: Check if image exists | |
| id: check-image | |
| run: | | |
| if aws ecr describe-images --repository-name performancetest --image-ids imageTag=${{ github.sha }} > /dev/null 2>&1; then | |
| echo "Docker image with given tag already exists" | |
| else | |
| echo "Docker image does not exist. Build needed" | |
| echo "build-needed=true" >> $GITHUB_OUTPUT | |
| fi | |
| build-and-push: | |
| needs: check-image-presence | |
| if: needs.check-image-presence.outputs.build-needed == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: performance-tests/ | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@v5 | |
| with: | |
| role-to-assume: arn:aws:iam::393416225559:role/GitHubAssuranceTestRole | |
| aws-region: eu-west-2 | |
| - name: Cache base jmeter image | |
| id: jmeter-base | |
| uses: actions/cache@v5 | |
| env: | |
| cache-name: cache-jmeter | |
| with: | |
| path: ${{ runner.temp }}/jmeter-image.tar | |
| key: jmeter-image-${{ hashFiles('performance-tests/base.Dockerfile') }} | |
| - name: Build base jmeter image | |
| if: steps.jmeter-base.outputs.cache-hit != 'true' | |
| run: | | |
| docker build -t base-jmeter-image:latest -f base.Dockerfile . | |
| docker save -o ${{ runner.temp }}/jmeter-image.tar base-jmeter-image:latest | |
| - name: Login to ECR | |
| id: login-ecr | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| - name: Build and push performancetest docker image | |
| run: | | |
| docker load -i ${{ runner.temp }}/jmeter-image.tar | |
| docker build -t "393416225559.dkr.ecr.eu-west-2.amazonaws.com/performancetest:${{ github.sha }}" . | |
| docker push "393416225559.dkr.ecr.eu-west-2.amazonaws.com/performancetest:${{ github.sha }}" | |
| run-performance-test: | |
| needs: [build-and-push, check-image-presence] | |
| if: ${{ !cancelled() && | |
| (needs.build-and-push.result == 'success' || | |
| (needs.check-image-presence.result == 'success' && needs.build-and-push.result == 'skipped') | |
| ) }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: write | |
| timeout-minutes: 180 | |
| env: | |
| RESULT_PATH: ${{ github.run_number }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set timestamp | |
| run: echo "timestamp=$(date '+%Y%m%d%H%M%S')" >> $GITHUB_ENV | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v5 | |
| with: | |
| role-to-assume: arn:aws:iam::393416225559:role/GitHubAssuranceTestRole | |
| aws-region: eu-west-2 | |
| role-duration-seconds: 14400 # 4 hours | |
| - name: Create task definition | |
| id: create-task-definition | |
| uses: aws-actions/amazon-ecs-render-task-definition@v1 | |
| with: | |
| task-definition-family: "assurance-testing-performance-task-definition-template" | |
| container-name: "performancetest-container" | |
| image: "393416225559.dkr.ecr.eu-west-2.amazonaws.com/performancetest:${{ github.sha }}" | |
| environment-variables: | | |
| ADD_NEW_SESSION=${{ inputs.AddNewSession }} | |
| RUN_CONSENT=${{ inputs.runConsent }} | |
| RUN_NURSE=${{ inputs.runNurse }} | |
| RESULT_PATH=${{ env.RESULT_PATH }} | |
| BASE_URL=${{ inputs.BaseURL }} | |
| USER=${{ inputs.user }} | |
| DURATION=${{ inputs.duration }} | |
| THREADS=${{ inputs.threads }} | |
| RAMP_UP=${{ inputs.ramp_up }} | |
| secrets: | | |
| AUTH_TOKEN=arn:aws:secretsmanager:eu-west-2:393416225559:secret:performancetest/auth-token | |
| - name: Register task definition | |
| id: register-task-definition | |
| run: | | |
| file_path="assurance-testing-performance-task-definition.json" | |
| family_name="assurance-testing-performance-task-definition" | |
| echo "$(jq --arg f "$family_name" '.family = $f' "${{ steps.create-task-definition.outputs.task-definition }}")" > "$file_path" | |
| task_definition_arn=$(aws ecs register-task-definition \ | |
| --cli-input-json file://$file_path \ | |
| --query 'taskDefinition.taskDefinitionArn' \ | |
| --output text | |
| ) | |
| echo "task_definition_arn=$task_definition_arn" >> $GITHUB_OUTPUT | |
| - name: Start performance test | |
| id: run-task | |
| run: | | |
| echo "Starting ECS task to run performance test against ${{ inputs.BaseURL }}" | |
| subnet_id=$(aws ec2 describe-subnets --filters Name=tag:Name,Values=assurance-testing-subnet --query 'Subnets[0].SubnetId' --output text) | |
| security_group_id=$(aws ec2 describe-security-groups --filters Name=group-name,Values=assurance-testing-performance-sg --query 'SecurityGroups[0].GroupId' --output text) | |
| task_arn=$(aws ecs run-task \ | |
| --cluster assurance-testing \ | |
| --task-definition ${{ steps.register-task-definition.outputs.task_definition_arn }} \ | |
| --launch-type FARGATE \ | |
| --network-configuration "awsvpcConfiguration={subnets=[$subnet_id],securityGroups=[$security_group_id],assignPublicIp=ENABLED}" \ | |
| --overrides '{ | |
| "containerOverrides": [{ | |
| "name": "performancetest-container" | |
| }] | |
| }' \ | |
| --query 'tasks[0].taskArn' \ | |
| --enable-execute-command \ | |
| --output text) | |
| echo "task_arn=$task_arn" >> $GITHUB_OUTPUT | |
| echo "test_start_time=$(date +%s)000" >> $GITHUB_ENV | |
| echo "Started task: $task_arn" | |
| - name: Wait for performance test to complete | |
| id: wait-for-completion | |
| run: | | |
| echo "Waiting for task to complete: ${{ steps.run-task.outputs.task_arn }}" | |
| TASK_ID=$(sed 's:^.*/::' <<< ${{ steps.run-task.outputs.task_arn }}) | |
| CLOUDWATCH_URL="https://eu-west-2.console.aws.amazon.com/cloudwatch/home?region=eu-west-2#logsV2:log-groups/log-group/assurance-testing-ecs/log-events/assurance-testing-logs\$252Fperformancetest-container\$252F${TASK_ID}\$3Fstart\$3D${test_start_time}\$26end\$3D$(date -d '+1 hour + 15 minutes' +%s)000" | |
| echo "Performance test logs: $CLOUDWATCH_URL" | |
| echo "CLOUDWATCH_URL=$CLOUDWATCH_URL" >> $GITHUB_ENV | |
| while true; do | |
| task_status=$(aws ecs describe-tasks \ | |
| --cluster assurance-testing \ | |
| --tasks ${{ steps.run-task.outputs.task_arn }} \ | |
| --query 'tasks[0].lastStatus' \ | |
| --output text) | |
| echo "Current task status: $task_status" | |
| if [ "$task_status" == "STOPPED" ]; then | |
| echo "Task has stopped" | |
| break | |
| fi | |
| sleep 30 # Check every 30 seconds | |
| done | |
| exit_code=$(aws ecs describe-tasks \ | |
| --cluster assurance-testing \ | |
| --tasks ${{ steps.run-task.outputs.task_arn }} \ | |
| --query 'tasks[0].containers[0].exitCode' \ | |
| --output text) | |
| echo "Container exit code: $exit_code" | |
| if [ "$exit_code" != "0" ]; then | |
| echo "ECS task failed with exit code: $exit_code" | |
| exit 1 | |
| fi | |
| - name: Collect test result | |
| id: collect-results | |
| run: | | |
| echo "Downloading test results from S3" | |
| S3_PATH="s3://performancetest-reports/${{ env.RESULT_PATH }}" | |
| aws s3 sync "${S3_PATH}" ./test-results --no-progress | |
| - name: Upload nurse journey JMeter output | |
| if: inputs.runNurse == true | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: jmeter-nurse-journey-output-${{ env.timestamp }} | |
| path: test-results/nurse | |
| if-no-files-found: warn | |
| - name: Upload consent journey JMeter output | |
| if: inputs.runConsent == true | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: jmeter-consent-journey-output-${{ env.timestamp }} | |
| path: test-results/consent | |
| if-no-files-found: warn | |
| - name: Publish report to GH Pages | |
| if: inputs.runNurse == true | |
| uses: peaceiris/actions-gh-pages@v4 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| publish_branch: gh-pages | |
| publish_dir: test-results/nurse/report | |
| destination_dir: JMeter/${{ env.RESULT_PATH }} | |
| keep_files: true | |
| - name: Set Job Summary | |
| run: | | |
| # Extract task ID from task ARN | |
| TASK_ARN="${{ steps.run-task.outputs.task_arn }}" | |
| TASK_ID=$(echo $TASK_ARN | awk -F/ '{print $NF}') | |
| # Construct CloudWatch Logs URL | |
| LOG_GROUP="/ecs/performancetest-task-definition" | |
| LOG_STREAM="ecs/performancetest-container/${TASK_ID}" | |
| # URL encode the log group and stream names | |
| ENCODED_LOG_GROUP=$(echo "$LOG_GROUP" | sed 's/\//%252F/g') | |
| ENCODED_LOG_STREAM=$(echo "$LOG_STREAM" | sed 's/\//%252F/g') | |
| CLOUDWATCH_URL="https://eu-west-2.console.aws.amazon.com/cloudwatch/home?region=eu-west-2#logsV2:log-groups/log-group/${ENCODED_LOG_GROUP}/log-events/${ENCODED_LOG_STREAM}" | |
| echo "## Performance Test Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **BaseURL:** ${{ inputs.BaseURL }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Run Number:** ${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Test Logs:** $CLOUDWATCH_URL" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "📊 [View Test Report](https://nhsdigital.github.io/manage-vaccinations-in-schools-testing/JMeter/${{ env.RESULT_PATH }}/)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "📋 [View CloudWatch Logs](${CLOUDWATCH_URL})" >> $GITHUB_STEP_SUMMARY |