<reponame>James96315/aws-serverless-workshop-greater-china-region
import subprocess
import os
import tempfile
import json
import json
import traceback
import logging
import shutil
from datetime import datetime
from uuid import uuid4
import urllib3

logger = logging.getLogger()
logger.setLevel(logging.INFO)
http = urllib3.PoolManager()

CFN_SUCCESS = "SUCCESS"
CFN_FAILED = "FAILED"

def handler(event, context):

    def cfn_error(message=None):
        logger.error("| cfn_error: %s" % message)
        cfn_send(event, context, CFN_FAILED, reason=message)

    try:
        logger.info(event)

        # cloudformation request type (create/update/delete)
        request_type = event['RequestType']

        # extract resource properties
        props = event['ResourceProperties']
        old_props = event.get('OldResourceProperties', {})
        physical_id = event.get('PhysicalResourceId', None)

        try:
            bucket_name = props['BucketName']
            object_key  = props['ObjectKey']
        except KeyError as e:
            cfn_error("missing request resource property %s. props: %s" % (str(e), props))
            return

        s3_source_file = f"s3://{bucket_name}/{object_key}"
        dynamodb_json_name = str(uuid4())

        logger.info(f"| s3_source: {s3_source_file}")
        logger.info(f"| s3_dest: {dynamodb_json_name}")

        # if we are creating a new resource, allocate a physical id for it
        # otherwise, we expect physical id to be relayed by cloudformation
        if request_type == "Create":
            physical_id = f"serverless.lab6.{str(uuid4())}"
        else:
            if not physical_id:
                cfn_error(f"invalid request: request type is '{request_type}' but 'PhysicalResourceId' is not defined")
                return

        # delete or create/update 
        if request_type == "Delete":
            pass

        if request_type == "Update":
            # do not support so far
            pass

        if request_type == "Update" or request_type == "Create":
            aws_command("s3", "cp", s3_source_file, dynamodb_json_name)
            aws_command("dynamodb", "batch-write-item", "--request-items", f"file:///tmp/{dynamodb_json_name}")

        cfn_send(event, context, CFN_SUCCESS, physicalResourceId=physical_id)
    except KeyError as e:
        cfn_error("invalid request. Missing key %s" % str(e))
    except Exception as e:
        logger.exception(e)
        cfn_error(str(e))

#---------------------------------------------------------------------------------------------------
# executes an "aws" cli command
def aws_command(*args):
    aws=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'aws')
    logger.info("| aws %s" % ' '.join(args))
    subprocess.check_call(["python3", aws] + list(args), cwd='/tmp/')

#---------------------------------------------------------------------------------------------------
# sends a response to cloudformation
def cfn_send(event, context, responseStatus, responseData={}, physicalResourceId=None, noEcho=False, reason=None):

    responseUrl = event['ResponseURL']
    logger.info(responseUrl)

    responseBody = {}
    responseBody['Status'] = responseStatus
    responseBody['Reason'] = reason or ('See the details in CloudWatch Log Stream: ' + context.log_stream_name)
    responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
    responseBody['StackId'] = event['StackId']
    responseBody['RequestId'] = event['RequestId']
    responseBody['LogicalResourceId'] = event['LogicalResourceId']
    responseBody['NoEcho'] = noEcho
    responseBody['Data'] = responseData

    body = json.dumps(responseBody)
    logger.info("| response body:\n" + body)

    headers = {
        'content-type' : '',
        'content-length' : str(len(body))
    }

    try:
        response = http.request('PUT',
                                responseUrl,
                                body=body,
                                headers=headers,
                                retries=False)
        logger.info("| status code: " + str(response.status))
    except Exception as e:
        logger.error("| unable to send response to CloudFormation")
        logger.exception(e)
import subprocess
import os
import tempfile
import json
import json
import traceback
import logging
import shutil
from datetime import datetime
from uuid import uuid4
import urllib3

logger = logging.getLogger()
logger.setLevel(logging.INFO)
http = urllib3.PoolManager()

CFN_SUCCESS = "SUCCESS"
CFN_FAILED = "FAILED"

def handler(event, context):

    def cfn_error(message=None):
        logger.error("| cfn_error: %s" % message)
        cfn_send(event, context, CFN_FAILED, reason=message)

    try:
        logger.info(event)

        # cloudformation request type (create/update/delete)
        request_type = event['RequestType']

        # extract resource properties
        props = event['ResourceProperties']
        old_props = event.get('OldResourceProperties', {})
        physical_id = event.get('PhysicalResourceId', None)

        try:
            bucket_name = props['BucketName']
            object_key  = props['ObjectKey']
        except KeyError as e:
            cfn_error("missing request resource property %s. props: %s" % (str(e), props))
            return

        s3_source_file = f"s3://{bucket_name}/{object_key}"
        dynamodb_json_name = str(uuid4())

        logger.info(f"| s3_source: {s3_source_file}")
        logger.info(f"| s3_dest: {dynamodb_json_name}")

        # if we are creating a new resource, allocate a physical id for it
        # otherwise, we expect physical id to be relayed by cloudformation
        if request_type == "Create":
            physical_id = f"serverless.lab6.{str(uuid4())}"
        else:
            if not physical_id:
                cfn_error(f"invalid request: request type is '{request_type}' but 'PhysicalResourceId' is not defined")
                return

        # delete or create/update 
        if request_type == "Delete":
            pass

        if request_type == "Update":
            # do not support so far
            pass

        if request_type == "Update" or request_type == "Create":
            aws_command("s3", "cp", s3_source_file, dynamodb_json_name)
            aws_command("dynamodb", "batch-write-item", "--request-items", f"file:///tmp/{dynamodb_json_name}")

        cfn_send(event, context, CFN_SUCCESS, physicalResourceId=physical_id)
    except KeyError as e:
        cfn_error("invalid request. Missing key %s" % str(e))
    except Exception as e:
        logger.exception(e)
        cfn_error(str(e))

#---------------------------------------------------------------------------------------------------
# executes an "aws" cli command
def aws_command(*args):
    aws=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'aws')
    logger.info("| aws %s" % ' '.join(args))
    subprocess.check_call(["python3", aws] + list(args), cwd='/tmp/')

#---------------------------------------------------------------------------------------------------
# sends a response to cloudformation
def cfn_send(event, context, responseStatus, responseData={}, physicalResourceId=None, noEcho=False, reason=None):

    responseUrl = event['ResponseURL']
    logger.info(responseUrl)

    responseBody = {}
    responseBody['Status'] = responseStatus
    responseBody['Reason'] = reason or ('See the details in CloudWatch Log Stream: ' + context.log_stream_name)
    responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
    responseBody['StackId'] = event['StackId']
    responseBody['RequestId'] = event['RequestId']
    responseBody['LogicalResourceId'] = event['LogicalResourceId']
    responseBody['NoEcho'] = noEcho
    responseBody['Data'] = responseData

    body = json.dumps(responseBody)
    logger.info("| response body:\n" + body)

    headers = {
        'content-type' : '',
        'content-length' : str(len(body))
    }

    try:
        response = http.request('PUT',
                                responseUrl,
                                body=body,
                                headers=headers,
                                retries=False)
        logger.info("| status code: " + str(response.status))
    except Exception as e:
        logger.error("| unable to send response to CloudFormation")
        logger.exception(e)
# cloudformation request type (create/update/delete) # extract resource properties # if we are creating a new resource, allocate a physical id for it # otherwise, we expect physical id to be relayed by cloudformation # delete or create/update # do not support so far #--------------------------------------------------------------------------------------------------- # executes an "aws" cli command #--------------------------------------------------------------------------------------------------- # sends a response to cloudformation