Skip to content

Commit 0c46a38

Browse files
authored
Merge pull request #824 from cisagov/CRASM_2286_Github_Actions_Dockerized_Playwright
CRASM-2286:Add Regression Test Workflow with Dockerized Playwright in AWS
2 parents 1092d77 + 30f4284 commit 0c46a38

File tree

8 files changed

+532
-291
lines changed

8 files changed

+532
-291
lines changed

.github/workflows/regression.yml

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
name: Regression Testing
3+
4+
on:
5+
push:
6+
branches:
7+
- develop
8+
- integration
9+
paths:
10+
- backend/**
11+
- .github/workflows/backend.yml
12+
pull_request:
13+
branches:
14+
- develop
15+
- integration
16+
paths:
17+
- backend/**
18+
- .github/workflows/backend.yml
19+
20+
defaults:
21+
run:
22+
working-directory: ./playwright
23+
24+
jobs:
25+
test_staging:
26+
runs-on: ubuntu-latest
27+
environment: staging
28+
timeout-minutes: 60
29+
steps:
30+
- uses: actions/checkout@v3
31+
- uses: actions/setup-node@v3
32+
with:
33+
node-version: 18
34+
35+
- name: Install dependencies
36+
run: npm ci
37+
38+
- name: Run Playwright tests
39+
env:
40+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
41+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
42+
PW_XFD_2FA_ISSUER: ${{ secrets.PW_XFD_2FA_ISSUER }}
43+
PW_XFD_2FA_SECRET: ${{ secrets.PW_XFD_2FA_SECRET }}
44+
PW_XFD_PASSWORD: ${{ secrets.PW_XFD_PASSWORD }}
45+
PW_XFD_USERNAME: ${{ secrets.PW_XFD_USERNAME }}
46+
run: |
47+
aws ecs run-task \
48+
--cluster crossfeed-playwright-staging-ecs-cluster \
49+
--task-definition playwright \
50+
--launch-type FARGATE \
51+
--network-configuration \
52+
"awsvpcConfiguration={subnets=[\"${{ secrets.AWS_SUBNET }}\"], \
53+
securityGroups=[\"${{ secrets.AWS_SECURITY_GROUP }}\"], \
54+
assignPublicIp=\"ENABLED\"}" \
55+
--overrides '{\
56+
"containerOverrides":[{\
57+
"name":"playwright",\
58+
"command":["sh", "-c", \
59+
"npx playwright install && npx playwright test"]}]}'
60+
continue-on-error: false # Ensure it stops if the ECS task fails
61+
62+
- name: Upload test results as artifact
63+
uses: actions/upload-artifact@v2
64+
with:
65+
name: playwright-test-results
66+
path: ./test-results/*.json
67+
68+
- name: Upload HTML report to S3
69+
run: |
70+
aws s3 cp ./playwright-report/html \
71+
s3://${{ vars.AUTOMATED_TEST_REPORT_BUCKET_NAME }}/\
72+
playwright-reports/html/ --recursive || \
73+
{ echo "HTML report upload failed"; exit 1; }
74+
75+
- name: Upload JSON report to S3
76+
run: |
77+
if aws s3 cp ./playwright-report/results.json \
78+
s3://${{ vars.AUTOMATED_TEST_REPORT_BUCKET_NAME }}/\
79+
playwright-reports/results.json; then \
80+
echo "JSON report uploaded successfully."; \
81+
else \
82+
echo "Failed to upload JSON report to S3." >&2; \
83+
exit 1; \
84+
fi
85+
86+
test_integration:
87+
runs-on: ubuntu-latest
88+
environment: integration
89+
timeout-minutes: 60
90+
steps:
91+
- uses: actions/checkout@v3
92+
- uses: actions/setup-node@v3
93+
with:
94+
node-version: 18
95+
96+
- name: Install dependencies
97+
run: npm ci
98+
99+
- name: Run Playwright tests
100+
env:
101+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
102+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
103+
PW_XFD_2FA_ISSUER: ${{ secrets.PW_XFD_2FA_ISSUER }}
104+
PW_XFD_2FA_SECRET: ${{ secrets.PW_XFD_2FA_SECRET }}
105+
PW_XFD_PASSWORD: ${{ secrets.PW_XFD_PASSWORD }}
106+
PW_XFD_USERNAME: ${{ secrets.PW_XFD_USERNAME }}
107+
run: |
108+
aws ecs run-task \
109+
--cluster crossfeed-playwright-integration-ecs-cluster \
110+
--task-definition playwright \
111+
--launch-type FARGATE \
112+
--network-configuration \
113+
"awsvpcConfiguration={subnets=[\"${{ secrets.AWS_SUBNET }}\"], \
114+
securityGroups=[\"${{ secrets.AWS_SECURITY_GROUP }}\"], \
115+
assignPublicIp=\"ENABLED\"}" \
116+
--overrides '{"containerOverrides":\
117+
[{"name":"playwright",\
118+
"command":["sh", "-c", \
119+
"npx playwright install && npx playwright test"]}]}'
120+
continue-on-error: false # Ensure it stops if the ECS task fails
121+
122+
- name: Upload test results as artifact
123+
uses: actions/upload-artifact@v2
124+
with:
125+
name: playwright-test-results
126+
path: ./test-results/*.json
127+
128+
- name: Upload HTML report to S3
129+
run: |
130+
aws s3 cp ./playwright-report/html \
131+
s3://${{ vars.AUTOMATED_TEST_REPORT_BUCKET_NAME }}/\
132+
playwright-reports/html/ --recursive || \
133+
{ echo "HTML report upload failed"; exit 1; }
134+
135+
- name: Upload JSON report to S3
136+
run: |
137+
if aws s3 cp ./playwright-report/results.json \
138+
s3://${{ vars.AUTOMATED_TEST_REPORT_BUCKET_NAME }}/\
139+
playwright-reports/results.json; then \
140+
echo "JSON report uploaded successfully."; \
141+
else \
142+
echo "Failed to upload JSON report to S3." >&2; \
143+
exit 1; \
144+
fi

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ services:
160160
# - rabbitmq-data:/var/lib/rabbitmq
161161

162162
playwright:
163-
image: mcr.microsoft.com/playwright:v1.41.2-jammy
163+
image: mcr.microsoft.com/playwright:v1.50.1-jammy
164164
working_dir: /workspace
165165
volumes:
166166
- ./playwright:/workspace
@@ -174,7 +174,7 @@ services:
174174
- PW_XFD_USER_ROLE=${PW_XFD_USER_ROLE}
175175
- PW_XFD_USERNAME=${PW_XFD_USERNAME}
176176
command: >
177-
/bin/bash -c "npm ci && npx playwright test"
177+
/bin/bash -c "npm ci && npx playwright install --with-deps && npx playwright test"
178178
179179
volumes:
180180
postgres-data:

infrastructure/integration.tfvars

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,5 @@ matomo_availability_zone = "us-east-1a"
111111
create_email_sender_instance = false
112112
email_sender_instance_type = "t3.small"
113113
crossfeed-lz-sync_name = "crossfeed-integration-lz-sync"
114+
crossfeed_playwright = "crossfeed-playwright-integration"
115+
automated_test_reports_bucket_name = "cisa-crossfeed-integration-automated-test-reports"

infrastructure/playwright.tf

Lines changed: 116 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
resource "aws_iam_role" "playwright_worker_task_execution_role" {
2-
name = "playwright-worker-task-execution-role"
2+
name = "${var.crossfeed_playwright}-task-execution-role"
3+
assume_role_policy = jsonencode({
4+
Version = "2012-10-17"
5+
Statement = [
6+
{
7+
Action = "sts:AssumeRole"
8+
Principal = {
9+
Service = "ecs-tasks.amazonaws.com"
10+
}
11+
Effect = "Allow"
12+
}
13+
]
14+
})
15+
}
16+
17+
resource "aws_iam_role" "playwright_worker_task_role" {
18+
name = "${var.crossfeed_playwright}-worker-task-role"
319

420
assume_role_policy = jsonencode({
521
Version = "2012-10-17"
@@ -16,8 +32,8 @@ resource "aws_iam_role" "playwright_worker_task_execution_role" {
1632
}
1733

1834
resource "aws_iam_role_policy" "playwright_ecs_task_policy" {
19-
name = "playwright-ecs-task-policy"
20-
role = aws_iam_role.worker_task_role.id
35+
name = "${var.crossfeed_playwright}-ecs-task-policy"
36+
role = aws_iam_role.playwright_worker_task_role.id
2137

2238
policy = jsonencode({
2339
Version = "2012-10-17"
@@ -26,8 +42,8 @@ resource "aws_iam_role_policy" "playwright_ecs_task_policy" {
2642
Action = ["s3:ListBucket", "s3:GetObject", "s3:PutObject"]
2743
Effect = "Allow"
2844
Resource = [
29-
"arn:aws:s3:::${var.automated_test_report_bucket_name}", # ListBucket on the bucket itself
30-
"arn:aws:s3:::${var.automated_test_report_bucket_name}/*" # GetObject and PutObject on all objects within the bucket
45+
"arn:aws:s3:::${var.automated_test_reports_bucket_name}", # ListBucket on the bucket itself
46+
"arn:aws:s3:::${var.automated_test_reports_bucket_name}/*" # GetObject and PutObject on all objects within the bucket
3147
]
3248
}
3349
]
@@ -36,28 +52,7 @@ resource "aws_iam_role_policy" "playwright_ecs_task_policy" {
3652

3753
resource "aws_iam_role_policy_attachment" "playwright_ecs_execution_policy" {
3854
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
39-
role = aws_iam_role.playwright_worker_task_execution_role.name
40-
}
41-
42-
resource "aws_ecr_repository" "playwright" {
43-
name = "playwright-ui-testing"
44-
45-
image_scanning_configuration {
46-
scan_on_push = true
47-
}
48-
49-
image_tag_mutability = "MUTABLE"
50-
51-
encryption_configuration {
52-
encryption_type = "KMS"
53-
kms_key = aws_kms_key.key.arn
54-
}
55-
56-
tags = {
57-
Project = var.project
58-
Stage = var.stage
59-
Owner = "Crossfeed managed resource"
60-
}
55+
role = aws_iam_role.playwright_worker_task_execution_role.id
6156
}
6257

6358
resource "aws_ecs_task_definition" "playwright_worker" {
@@ -66,7 +61,7 @@ resource "aws_ecs_task_definition" "playwright_worker" {
6661
[
6762
{
6863
"name": "playwright",
69-
"image": "${aws_ecr_repository.playwright.repository_url}:${var.image_tag}",
64+
"image": "public.ecr.aws/sphmedia/sphmedia/microsoft-playwright:v1.50.1-jammy",
7065
"essential": true,
7166
"mountPoints": [],
7267
"portMappings": [],
@@ -93,9 +88,10 @@ resource "aws_ecs_task_definition" "playwright_worker" {
9388
]
9489
EOF
9590
requires_compatibilities = ["FARGATE"]
96-
network_mode = "awsvpc"
97-
execution_role_arn = aws_iam_role.playwright_worker_task_execution_role.arn
98-
task_role_arn = aws_iam_role.worker_task_role.arn
91+
# "awsvpc" is required for Fargate tasks to enable the use of ENIs for networking.
92+
network_mode = "awsvpc"
93+
execution_role_arn = aws_iam_role.playwright_worker_task_execution_role.arn # Execution role for ECS tasks
94+
task_role_arn = aws_iam_role.playwright_worker_task_role.arn # Task role for the application
9995

10096
cpu = 256 # .25 vCPU
10197
memory = 512 # 512 MB
@@ -106,3 +102,92 @@ EOF
106102
Owner = "Crossfeed managed resource"
107103
}
108104
}
105+
106+
resource "aws_ecs_cluster" "playwright_ecs_cluster" {
107+
name = "${var.crossfeed_playwright}-ecs-cluster"
108+
109+
setting {
110+
name = "containerInsights"
111+
value = "enabled"
112+
}
113+
114+
tags = {
115+
Project = var.project
116+
Stage = var.stage
117+
Owner = "Crossfeed managed resource"
118+
}
119+
}
120+
121+
resource "aws_ecs_cluster_capacity_providers" "playwright_ecs_cluster_capacity_providers" {
122+
cluster_name = aws_ecs_cluster.playwright_ecs_cluster.name
123+
capacity_providers = ["FARGATE"]
124+
}
125+
126+
resource "aws_s3_bucket" "automated_test_reports_bucket" {
127+
bucket = var.automated_test_reports_bucket_name
128+
tags = {
129+
Project = var.project
130+
Stage = var.stage
131+
Owner = "Crossfeed managed resource"
132+
}
133+
}
134+
135+
resource "aws_s3_bucket_policy" "automated_test_reports_bucket" {
136+
bucket = var.automated_test_reports_bucket_name
137+
policy = jsonencode({
138+
"Version" : "2012-10-17",
139+
"Statement" : [
140+
{
141+
"Sid" : "RequireSSLRequests",
142+
"Action" : "s3:*",
143+
"Effect" : "Deny",
144+
"Principal" : "*",
145+
"Resource" : [
146+
aws_s3_bucket.automated_test_reports_bucket.arn,
147+
"${aws_s3_bucket.automated_test_reports_bucket.arn}/*"
148+
],
149+
"Condition" : {
150+
"Bool" : {
151+
"aws:SecureTransport" : "false"
152+
}
153+
}
154+
}
155+
]
156+
})
157+
}
158+
159+
resource "aws_s3_bucket_acl" "automated_test_reports_bucket" {
160+
count = var.is_dmz ? 1 : 0
161+
bucket = aws_s3_bucket.automated_test_reports_bucket.id
162+
acl = "private"
163+
}
164+
165+
resource "aws_s3_bucket_ownership_controls" "automated_test_reports_bucket" {
166+
count = var.is_dmz ? 1 : 0
167+
bucket = aws_s3_bucket.automated_test_reports_bucket.id
168+
rule {
169+
object_ownership = "ObjectWriter"
170+
}
171+
}
172+
173+
resource "aws_s3_bucket_server_side_encryption_configuration" "automated_test_reports_bucket" {
174+
bucket = aws_s3_bucket.automated_test_reports_bucket.id
175+
rule {
176+
apply_server_side_encryption_by_default {
177+
sse_algorithm = "AES256"
178+
}
179+
}
180+
}
181+
182+
resource "aws_s3_bucket_versioning" "automated_test_reports_bucket" {
183+
bucket = aws_s3_bucket.automated_test_reports_bucket.id
184+
versioning_configuration {
185+
status = "Enabled"
186+
}
187+
}
188+
189+
resource "aws_s3_bucket_logging" "automated_test_reports_bucket" {
190+
bucket = aws_s3_bucket.automated_test_reports_bucket.id
191+
target_bucket = aws_s3_bucket.logging_bucket.id
192+
target_prefix = "automated_test_reports_bucket/"
193+
}

0 commit comments

Comments
 (0)