Skip to content

Commit 06c01d3

Browse files
committed
Add clean ecs persistant volume workflow
1 parent 6ea1d70 commit 06c01d3

File tree

1 file changed

+354
-0
lines changed

1 file changed

+354
-0
lines changed
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
name: Clean ECS Volume
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
environment:
7+
description: 'Environment (dev, staging, prod)'
8+
required: true
9+
default: 'dev'
10+
type: choice
11+
options:
12+
- dev
13+
region:
14+
description: 'AWS Region'
15+
required: true
16+
default: 'eu-central-1'
17+
type: string
18+
service_name:
19+
description: 'ECS Service name (without environment prefix)'
20+
required: true
21+
default: 'zebra'
22+
type: string
23+
24+
env:
25+
AWS_REGION: ${{ inputs.region || 'eu-central-1' }}
26+
27+
jobs:
28+
clean-volume:
29+
name: Clean ECS Volume
30+
runs-on: ubuntu-latest
31+
permissions:
32+
id-token: write
33+
contents: read
34+
35+
steps:
36+
- name: Configure AWS credentials
37+
uses: aws-actions/configure-aws-credentials@v4
38+
with:
39+
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
40+
aws-region: ${{ env.AWS_REGION }}
41+
42+
- name: Install dependencies
43+
run: |
44+
sudo apt-get update
45+
sudo apt-get install -y jq
46+
47+
- name: Set variables
48+
id: vars
49+
run: |
50+
ENVIRONMENT="${{ inputs.environment || 'dev' }}"
51+
SERVICE_NAME="${{ inputs.service_name || 'zebra' }}"
52+
CLUSTER_NAME="${ENVIRONMENT}-${SERVICE_NAME}-cluster"
53+
ECS_SERVICE_NAME="${ENVIRONMENT}-${SERVICE_NAME}"
54+
55+
echo "cluster_name=${CLUSTER_NAME}" >> $GITHUB_OUTPUT
56+
echo "ecs_service_name=${ECS_SERVICE_NAME}" >> $GITHUB_OUTPUT
57+
58+
echo "Cluster: ${CLUSTER_NAME}"
59+
echo "Service: ${ECS_SERVICE_NAME}"
60+
61+
- name: Get task definition and EFS details
62+
id: get-efs
63+
run: |
64+
# Get the task definition ARN from the service
65+
TASK_DEF_ARN=$(aws ecs describe-services \
66+
--cluster ${{ steps.vars.outputs.cluster_name }} \
67+
--services ${{ steps.vars.outputs.ecs_service_name }} \
68+
--query 'services[0].taskDefinition' \
69+
--output text)
70+
71+
echo "task_def_arn=${TASK_DEF_ARN}" >> $GITHUB_OUTPUT
72+
echo "Task Definition: ${TASK_DEF_ARN}"
73+
74+
# Get EFS file system ID and mount point from task definition
75+
EFS_FS_ID=$(aws ecs describe-task-definition \
76+
--task-definition "${TASK_DEF_ARN}" \
77+
--query 'taskDefinition.volumes[?name==`persistent-volume`].efsVolumeConfiguration.fileSystemId' \
78+
--output text)
79+
80+
MOUNT_PATH=$(aws ecs describe-task-definition \
81+
--task-definition "${TASK_DEF_ARN}" \
82+
--query 'taskDefinition.containerDefinitions[0].mountPoints[?sourceVolume==`persistent-volume`].containerPath' \
83+
--output text)
84+
85+
# Get subnet and security group from the service
86+
SUBNETS=$(aws ecs describe-services \
87+
--cluster ${{ steps.vars.outputs.cluster_name }} \
88+
--services ${{ steps.vars.outputs.ecs_service_name }} \
89+
--query 'services[0].networkConfiguration.awsvpcConfiguration.subnets' \
90+
--output text)
91+
92+
SECURITY_GROUPS=$(aws ecs describe-services \
93+
--cluster ${{ steps.vars.outputs.cluster_name }} \
94+
--services ${{ steps.vars.outputs.ecs_service_name }} \
95+
--query 'services[0].networkConfiguration.awsvpcConfiguration.securityGroups' \
96+
--output text)
97+
98+
# Use first subnet and security group
99+
SUBNET=$(echo $SUBNETS | awk '{print $1}')
100+
SECURITY_GROUP=$(echo $SECURITY_GROUPS | awk '{print $1}')
101+
102+
if [ -z "$EFS_FS_ID" ] || [ "$EFS_FS_ID" == "None" ]; then
103+
echo "Error: Could not find EFS file system ID in task definition"
104+
exit 1
105+
fi
106+
107+
echo "efs_fs_id=${EFS_FS_ID}" >> $GITHUB_OUTPUT
108+
echo "mount_path=${MOUNT_PATH:-/persistent}" >> $GITHUB_OUTPUT
109+
echo "subnet=${SUBNET}" >> $GITHUB_OUTPUT
110+
echo "security_group=${SECURITY_GROUP}" >> $GITHUB_OUTPUT
111+
112+
echo "EFS File System ID: ${EFS_FS_ID}"
113+
echo "Mount Path: ${MOUNT_PATH:-/persistent}"
114+
echo "Subnet: ${SUBNET}"
115+
echo "Security Group: ${SECURITY_GROUP}"
116+
117+
- name: Get execution role ARN
118+
id: get-role
119+
run: |
120+
EXEC_ROLE_ARN=$(aws ecs describe-task-definition \
121+
--task-definition ${{ steps.get-efs.outputs.task_def_arn }} \
122+
--query 'taskDefinition.executionRoleArn' \
123+
--output text)
124+
125+
TASK_ROLE_ARN=$(aws ecs describe-task-definition \
126+
--task-definition ${{ steps.get-efs.outputs.task_def_arn }} \
127+
--query 'taskDefinition.taskRoleArn' \
128+
--output text)
129+
130+
echo "execution_role_arn=${EXEC_ROLE_ARN}" >> $GITHUB_OUTPUT
131+
echo "task_role_arn=${TASK_ROLE_ARN:-${EXEC_ROLE_ARN}}" >> $GITHUB_OUTPUT
132+
133+
- name: Run cleanup task
134+
id: cleanup-task
135+
run: |
136+
echo "Running cleanup task to remove all files from ECS volume..."
137+
138+
# Create a temporary task definition JSON for the cleanup task
139+
CLEANUP_TASK_DEF=$(cat <<EOF
140+
{
141+
"family": "cleanup-ecs-volume",
142+
"networkMode": "awsvpc",
143+
"requiresCompatibilities": ["FARGATE"],
144+
"cpu": "256",
145+
"memory": "512",
146+
"executionRoleArn": "${{ steps.get-role.outputs.execution_role_arn }}",
147+
"taskRoleArn": "${{ steps.get-role.outputs.task_role_arn }}",
148+
"containerDefinitions": [
149+
{
150+
"name": "cleanup-container",
151+
"image": "alpine:latest",
152+
"essential": true,
153+
"command": [
154+
"sh",
155+
"-c",
156+
"echo 'Starting cleanup...' && rm -rf ${{ steps.get-efs.outputs.mount_path }}/* ${{ steps.get-efs.outputs.mount_path }}/.[!.]* ${{ steps.get-efs.outputs.mount_path }}/..?* 2>/dev/null || true && find ${{ steps.get-efs.outputs.mount_path }} -mindepth 1 -delete 2>/dev/null || true && echo 'Cleanup completed successfully' && ls -la ${{ steps.get-efs.outputs.mount_path }} || echo 'Directory is empty'"
157+
],
158+
"mountPoints": [
159+
{
160+
"sourceVolume": "persistent-volume",
161+
"containerPath": "${{ steps.get-efs.outputs.mount_path }}",
162+
"readOnly": false
163+
}
164+
],
165+
"logConfiguration": {
166+
"logDriver": "awslogs",
167+
"options": {
168+
"awslogs-group": "/${{ inputs.environment || 'dev' }}/ecs/cleanup-task",
169+
"awslogs-region": "${{ env.AWS_REGION }}",
170+
"awslogs-stream-prefix": "ecs"
171+
}
172+
}
173+
}
174+
],
175+
"volumes": [
176+
{
177+
"name": "persistent-volume",
178+
"efsVolumeConfiguration": {
179+
"fileSystemId": "${{ steps.get-efs.outputs.efs_fs_id }}",
180+
"rootDirectory": "/",
181+
"transitEncryption": "ENABLED"
182+
}
183+
}
184+
]
185+
}
186+
EOF
187+
)
188+
189+
# Register the task definition
190+
TASK_DEF_JSON=$(echo "$CLEANUP_TASK_DEF" | jq .)
191+
TASK_DEF_ARN=$(aws ecs register-task-definition \
192+
--cli-input-json "$TASK_DEF_JSON" \
193+
--query 'taskDefinition.taskDefinitionArn' \
194+
--output text)
195+
196+
echo "task_def_arn=${TASK_DEF_ARN}" >> $GITHUB_OUTPUT
197+
echo "Registered cleanup task definition: ${TASK_DEF_ARN}"
198+
199+
# Create CloudWatch log group if it doesn't exist
200+
aws logs create-log-group \
201+
--log-group-name "/${{ inputs.environment || 'dev' }}/ecs/cleanup-task" \
202+
2>/dev/null || true
203+
204+
# Run the cleanup task
205+
NETWORK_CONFIG=$(cat <<EOF
206+
{
207+
"awsvpcConfiguration": {
208+
"subnets": ["${{ steps.get-efs.outputs.subnet }}"],
209+
"securityGroups": ["${{ steps.get-efs.outputs.security_group }}"],
210+
"assignPublicIp": "DISABLED"
211+
}
212+
}
213+
EOF
214+
)
215+
216+
TASK_ARN=$(aws ecs run-task \
217+
--cluster ${{ steps.vars.outputs.cluster_name }} \
218+
--task-definition "${TASK_DEF_ARN}" \
219+
--launch-type FARGATE \
220+
--network-configuration "$(echo "$NETWORK_CONFIG" | jq -c .)" \
221+
--query 'tasks[0].taskArn' \
222+
--output text)
223+
224+
echo "task_arn=${TASK_ARN}" >> $GITHUB_OUTPUT
225+
echo "Started cleanup task: ${TASK_ARN}"
226+
227+
# Wait for the task to complete
228+
echo "Waiting for cleanup task to complete..."
229+
aws ecs wait tasks-stopped \
230+
--cluster ${{ steps.vars.outputs.cluster_name }} \
231+
--tasks "${TASK_ARN}"
232+
233+
# Get task exit code
234+
EXIT_CODE=$(aws ecs describe-tasks \
235+
--cluster ${{ steps.vars.outputs.cluster_name }} \
236+
--tasks "${TASK_ARN}" \
237+
--query 'tasks[0].containers[0].exitCode' \
238+
--output text)
239+
240+
echo "exit_code=${EXIT_CODE}" >> $GITHUB_OUTPUT
241+
242+
if [ "$EXIT_CODE" != "0" ] && [ "$EXIT_CODE" != "None" ]; then
243+
echo "Warning: Cleanup task exited with code ${EXIT_CODE}"
244+
echo "Checking logs..."
245+
aws logs tail "/${{ inputs.environment || 'dev' }}/ecs/cleanup-task" --since 5m || true
246+
else
247+
echo "Cleanup task completed successfully"
248+
fi
249+
250+
- name: Verify cleanup
251+
run: |
252+
echo "Verifying cleanup by checking EFS contents..."
253+
254+
# Run a verification task
255+
VERIFY_TASK_DEF=$(cat <<EOF
256+
{
257+
"family": "verify-cleanup",
258+
"networkMode": "awsvpc",
259+
"requiresCompatibilities": ["FARGATE"],
260+
"cpu": "256",
261+
"memory": "512",
262+
"executionRoleArn": "${{ steps.get-role.outputs.execution_role_arn }}",
263+
"taskRoleArn": "${{ steps.get-role.outputs.task_role_arn }}",
264+
"containerDefinitions": [
265+
{
266+
"name": "verify-container",
267+
"image": "alpine:latest",
268+
"essential": true,
269+
"command": [
270+
"sh",
271+
"-c",
272+
"echo 'Verifying cleanup...' && ls -la ${{ steps.get-efs.outputs.mount_path }} && echo 'Verification complete'"
273+
],
274+
"mountPoints": [
275+
{
276+
"sourceVolume": "persistent-volume",
277+
"containerPath": "${{ steps.get-efs.outputs.mount_path }}",
278+
"readOnly": true
279+
}
280+
],
281+
"logConfiguration": {
282+
"logDriver": "awslogs",
283+
"options": {
284+
"awslogs-group": "/${{ inputs.environment || 'dev' }}/ecs/cleanup-task",
285+
"awslogs-region": "${{ env.AWS_REGION }}",
286+
"awslogs-stream-prefix": "verify"
287+
}
288+
}
289+
}
290+
],
291+
"volumes": [
292+
{
293+
"name": "persistent-volume",
294+
"efsVolumeConfiguration": {
295+
"fileSystemId": "${{ steps.get-efs.outputs.efs_fs_id }}",
296+
"rootDirectory": "/",
297+
"transitEncryption": "ENABLED"
298+
}
299+
}
300+
]
301+
}
302+
EOF
303+
)
304+
305+
VERIFY_TASK_DEF_JSON=$(echo "$VERIFY_TASK_DEF" | jq .)
306+
VERIFY_TASK_DEF_ARN=$(aws ecs register-task-definition \
307+
--cli-input-json "$VERIFY_TASK_DEF_JSON" \
308+
--query 'taskDefinition.taskDefinitionArn' \
309+
--output text)
310+
311+
VERIFY_NETWORK_CONFIG=$(cat <<EOF
312+
{
313+
"awsvpcConfiguration": {
314+
"subnets": ["${{ steps.get-efs.outputs.subnet }}"],
315+
"securityGroups": ["${{ steps.get-efs.outputs.security_group }}"],
316+
"assignPublicIp": "DISABLED"
317+
}
318+
}
319+
EOF
320+
)
321+
322+
VERIFY_TASK_ARN=$(aws ecs run-task \
323+
--cluster ${{ steps.vars.outputs.cluster_name }} \
324+
--task-definition "${VERIFY_TASK_DEF_ARN}" \
325+
--launch-type FARGATE \
326+
--network-configuration "$(echo "$VERIFY_NETWORK_CONFIG" | jq -c .)" \
327+
--query 'tasks[0].taskArn' \
328+
--output text)
329+
330+
echo "Verification task: ${VERIFY_TASK_ARN}"
331+
332+
aws ecs wait tasks-stopped \
333+
--cluster ${{ steps.vars.outputs.cluster_name }} \
334+
--tasks "${VERIFY_TASK_ARN}"
335+
336+
echo "Verification logs:"
337+
aws logs tail "/${{ inputs.environment || 'dev' }}/ecs/cleanup-task" --since 2m --filter-pattern "verify" || true
338+
339+
- name: Cleanup summary
340+
if: always()
341+
run: |
342+
echo "## Cleanup Summary" >> $GITHUB_STEP_SUMMARY
343+
echo "- Cluster: ${{ steps.vars.outputs.cluster_name }}" >> $GITHUB_STEP_SUMMARY
344+
echo "- Service: ${{ steps.vars.outputs.ecs_service_name }}" >> $GITHUB_STEP_SUMMARY
345+
echo "- EFS File System: ${{ steps.get-efs.outputs.efs_fs_id }}" >> $GITHUB_STEP_SUMMARY
346+
echo "- Mount Path: ${{ steps.get-efs.outputs.mount_path }}" >> $GITHUB_STEP_SUMMARY
347+
echo "- Cleanup Task: ${{ steps.cleanup-task.outputs.task_arn }}" >> $GITHUB_STEP_SUMMARY
348+
echo "- Exit Code: ${{ steps.cleanup-task.outputs.exit_code }}" >> $GITHUB_STEP_SUMMARY
349+
350+
if [ "${{ steps.cleanup-task.outputs.exit_code }}" == "0" ] || [ "${{ steps.cleanup-task.outputs.exit_code }}" == "None" ]; then
351+
echo "✅ ECS volume cleaned successfully" >> $GITHUB_STEP_SUMMARY
352+
else
353+
echo "⚠️ Cleanup completed with warnings. Check logs for details." >> $GITHUB_STEP_SUMMARY
354+
fi

0 commit comments

Comments
 (0)