123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- #
- # 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')
|