You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/scripts/build/lambda/delete_github_branch_ebs.py

115 lines
4.6 KiB
Python

#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import os
import boto3
import json
import hmac
import hashlib
def delete_volumes(repository_name, branch_name):
"""
Trigger lambda function that deletes EBS volumes.
:param repository_name: Full repository name.
:param branch_name: Branch name that is deleted.
:return: Number of EBS volumes that are deleted successfully, number of EBS volumes that are not deleted.
"""
client = boto3.client('lambda')
payload = {
'repository_name': repository_name,
'branch_name': branch_name
}
response = client.invoke(
FunctionName='delete_branch_ebs',
Payload=json.dumps(payload),
)
status = response['Payload'].read()
response_json = json.loads(status.decode())
return response_json['success'], response_json['failure']
def verify_signature(headers, payload):
"""
Validate POST request headers and payload to only receive the expected GitHub webhook requests.
:param headers: Headers from POST request.
:param payload: Payload from POST request.
:return: True if request is verified, otherwise, return False.
"""
# secret is stored in AWS Secret Manager
secret_name = os.environ.get('GITHUB_WEBHOOK_SECRET_NAME', '')
client = boto3.client(service_name='secretsmanager')
response = client.get_secret_value(SecretId=secret_name)
secret = response['SecretString']
# Using X-Hub-Signature-256 is recommended by https://docs.github.com/en/developers/webhooks-and-events/securing-your-webhooks
signature = headers.get('X-Hub-Signature-256', '')
computed_hash = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
computed_signature = f'sha256={computed_hash}'
return hmac.compare_digest(computed_signature.encode(), signature.encode())
def create_response(status, success=0, failure=0, repository_name=None, branch_name=None):
"""
:param status: Status of EBS deletion request.
:param success: Number of EBS volumes that are deleted successfully.
:param failure: Number of EBS volumes that are not deleted.
:param repository_name: Full repository name.
:param branch_name: Branch name that is deleted.
:return: JSON response.
"""
response = {
'success': {
'statusCode': 200,
'body': f'[SUCCESS] All {success + failure} EBS volumes are deleted for branch {branch_name} in repository {repository_name}',
'isBase64Encoded': 'false'
},
'failure': {
'statusCode': 500,
'body': f'[FAILURE] Failed to delete {failure}/{success + failure} EBS volumes for branch {branch_name} in repository {repository_name}',
'isBase64Encoded': 'false'
},
'unauthorized': {
'statusCode': 401,
'body': 'Unauthorized',
'isBase64Encoded': 'false'
},
'unsupported': {
'statusCode': 204,
'isBase64Encoded': 'false'
}
}
return response[status]
def lambda_handler(event, context):
# This function is triggered by AWS API Gateway,
if event.get('resource', '') == '/delete-github-branch-ebs':
headers = event['headers']
payload = event['body']
# Validate github webhook request here since request body cannot be passed to API Gateway lambda authorizer.
if verify_signature(headers, payload):
# Convert payload from string type to json to get repository name and branch name
payload = json.loads(payload)
repository_name = payload['repository']['full_name']
if headers['X-GitHub-Event'] == 'delete':
# On Github branch/tag delete event
branch_name = payload['ref']
elif headers['X-GitHub-Event'] == 'pull_request' and payload['action'] == 'closed':
# On Github pull request closed event
pull_request_number = payload['number']
branch_name = f'PR-{pull_request_number}'
else:
return create_response('unsupported')
(success, failure) = delete_volumes(repository_name, branch_name)
if not failure:
return create_response('success', success, failure, repository_name, branch_name)
else:
return create_response('failure', success, failure, repository_name, branch_name)
else:
return create_response('unauthorized')