"""Lambda to begin step machine execution upon S3 trigger.""" import json import os from datetime import datetime import boto3 from botocore.exceptions import ClientError from s3_image import get_image_from_s3, save_image_to_s3 from s3_image import S3ImageUploadFailed from util import which from wav_utils import check_wav, get_wav_from_s3, save_wav_to_s3, \ WavFileCheckFailure, WavFileConverted, WavFileFixFailure, \ WavFileOpenFailure, WavFileUploadFailure, WavFileWriteFailure def generate_step_params(bucket, key): """Create a set of parameters to pass to sfn::startExecution(). Args: bucket (str): The bucket containing the object key (str): The key of the object Returns: dict: A dictionary of properties of the object. """ # Get S3 object s3_client = boto3.client('s3') response = s3_client.head_object(Bucket=bucket, Key=key) # Debug - Show response content print('Head_object: {}'.format(response)) key_name, key_ext = os.path.splitext(key) path, path_ext = os.path.splitext(response['Metadata']['path']) if key_ext == '.jpg': key = key_name + '.tif' response['Metadata']['path'] = path + '.tif' return { 'upc': response['Metadata']['upc'], 's3_bucket': bucket, 's3_key': key, 'host': response['Metadata']['host'], 'destination_filepath': response['Metadata']['path'], 'destination_filename': response['Metadata']['path'].split('/')[-1], 'sharename': response['Metadata']['sharename'] } def process_image_file(bucket, key, key_name): """Process an image file.""" print('Getting JPG to convert to TIF') # Convert to TIF img = get_image_from_s3(bucket, key) tif_key = key_name + '.tif' print('Transform and Save image: {}'.format(tif_key)) save_image_to_s3(img, bucket, tif_key, dpi=(300, 300)) def process_s3_event(record): """Parse S3 event, create SFN execution params, and execute SFN.""" # Get environment vars env = os.getenv('ENVIRONMENT', 'dev') service_name = os.getenv('SERVICE_NAME', '') region = os.getenv('REGION', '') account_id = os.getenv('ACCOUNT_ID', '') get_solution = '' check_solution = '' # Define local vars time_at_call = datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S.%f') s3_record = record.get('s3') bucket = s3_record.get('bucket', {}).get('name') key = s3_record.get('object', {}).get('key') sfn_to_call = '{}-{}-sfn'.format(env, service_name) print('Got key: {}'.format(key)) key_name, ext = os.path.splitext(key) if ext == '.jpg': print('Processing image file: {}'.format(bucket + '/' + key)) try: process_image_file(bucket, key, key_name) except S3ImageUploadFailed as e: print(str(e)) return elif ext == '.wav': print('Checking for `sox`.') if not (which('sox')): print('`sox` not found. Conversion is not possible. Please ' 'install sox lambda layer to make `/opt/bin/sox` available. ' 'Passing WAV file as is.') else: print('`sox` found. Checking WAV file for ripper compatibility.') try: temp_wav_file, get_fixed, get_solution = get_wav_from_s3( bucket, key) temp_wav_file, check_fixed, check_solution = check_wav( temp_wav_file, bucket, key) if get_fixed or check_fixed: print('Saving fixed wav.') save_wav_to_s3( temp_wav_file, bucket, key, ' - '.join( [get_solution, check_solution])) print('Exiting Lambda.') return else: print('No changes needed.') except WavFileOpenFailure as w: print('File open failed: {}'.format(str(w))) print('Exiting Lambda.') return except WavFileCheckFailure as w: print('File check failed: {}'.format(str(w))) print('Exiting Lambda.') return except WavFileUploadFailure as w: print('File upload failed: {}'.format(str(w))) print('Exiting Lambda.') return except WavFileWriteFailure as w: print('File write failed: {}'.format(str(w))) print('Exiting Lambda.') return except WavFileFixFailure as w: print('File fix failed: {}'.format(str(w))) print('Exiting Lambda.') return except WavFileConverted: print('File is already converted') except Exception as e: print(str(e)) return print('Generating Step Params') step_params = generate_step_params(bucket, key) # DEBUG - Show step machine parameters content print(json.dumps(step_params, indent=2)) client = boto3.client('stepfunctions') sfn_arn = 'arn:aws:states:{}:{}:stateMachine:{}'.format( region, account_id, sfn_to_call) response = client.start_execution( stateMachineArn=sfn_arn, name='{}-exec-{}'.format(sfn_to_call, time_at_call), input=json.dumps(step_params) ) # Report success to console print("Step Function '{}' invoked!".format(sfn_to_call)) print('Execution response: {}'.format(response)) def handler(event, context): """Lambda handler entry point.""" # DEBUG - Show event content print(json.dumps(event, indent=2)) # Process event # if event.get('Records'): # Sanity check record_len = len(event.get('Records')) if record_len < 1: print("No records in 'Records' field of event.") for record in event.get('Records'): event_source = record.get('eventSource') print('Event received from {}'.format(event_source)) if event_source == 'aws:s3': try: process_s3_event(record) except ClientError as e: print(str(e)) else: print( "Event source '{}' is not handled in this " 'lambda.'.format(event_source))