def get_lambda_code_param(params, _include_arch=False, **kwargs): code = params.get("Code", {}) zip_file = code.get("ZipFile") if zip_file and not is_base64(zip_file) and not is_zip_file( to_bytes(zip_file)): tmp_dir = new_tmp_dir() handler_file = get_handler_file_from_name( params["Handler"], runtime=params["Runtime"]) tmp_file = os.path.join(tmp_dir, handler_file) save_file(tmp_file, zip_file) # add 'cfn-response' module to archive - see: # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html cfn_response_tmp_file = get_cfn_response_mod_file() cfn_response_mod_dir = os.path.join(tmp_dir, "node_modules", "cfn-response") mkdir(cfn_response_mod_dir) cp_r( cfn_response_tmp_file, os.path.join(cfn_response_mod_dir, "index.js"), ) # create zip file zip_file = create_zip_file(tmp_dir, get_content=True) code["ZipFile"] = zip_file rm_rf(tmp_dir) if _include_arch and "Architectures" in params: code["Architectures"] = params.get("Architectures") return code
def get_java_handler(zip_file_content, handler, main_file): """Creates a Java handler from an uploaded ZIP or JAR. :type zip_file_content: bytes :param zip_file_content: ZIP file bytes. :type handler: str :param handler: The lambda handler path. :type main_file: str :param main_file: Filepath to the uploaded ZIP or JAR file. :returns: function or flask.Response """ if is_zip_file(zip_file_content): def execute(event, context): result, log_output = lambda_executors.EXECUTOR_LOCAL.execute_java_lambda( event, context, handler=handler, main_file=main_file) return result return execute raise ClientError(error_response( 'Unable to extract Java Lambda handler - file is not a valid zip/jar file', 400, error_type='ValidationError'))
def get_java_handler(zip_file_content, handler, main_file): """Creates a Java handler from an uploaded ZIP or JAR. :type zip_file_content: bytes :param zip_file_content: ZIP file bytes. :type handler: str :param handler: The lambda handler path. :type main_file: str :param main_file: Filepath to the uploaded ZIP or JAR file. :returns: function or flask.Response """ if not is_jar_archive(zip_file_content): with zipfile.ZipFile(BytesIO(zip_file_content)) as zip_ref: # TODO: check if this is still needed (probably not) jar_entries = [ e for e in zip_ref.infolist() if e.filename.endswith('.jar') ] if len(jar_entries) == 1: zip_file_content = zip_ref.read(jar_entries[0].filename) LOG.info( 'Found single jar file %s with %s bytes in Lambda zip archive' % (jar_entries[0].filename, len(zip_file_content))) main_file = new_tmp_file() save_file(main_file, zip_file_content) if is_zip_file(zip_file_content): def execute(event, context): result, log_output = lambda_executors.EXECUTOR_LOCAL.execute_java_lambda( event, context, handler=handler, main_file=main_file) return result return execute, zip_file_content raise ClientError( error_response( 'Unable to extract Java Lambda handler - file is not a valid zip/jar file', 400, error_type='ValidationError'))
def set_function_code(code, lambda_name): def generic_handler(event, context): raise Exception(('Unable to find executor for Lambda function "%s". ' + 'Note that Node.js and .NET Core Lambdas currently require LAMBDA_EXECUTOR=docker') % lambda_name) lambda_cwd = None arn = func_arn(lambda_name) lambda_details = arn_to_lambda[arn] runtime = lambda_details.runtime handler_name = lambda_details.handler lambda_environment = lambda_details.envvars if not handler_name: handler_name = LAMBDA_DEFAULT_HANDLER # Stop/remove any containers that this arn uses. LAMBDA_EXECUTOR.cleanup(arn) zip_file_content = None is_local_mount = code.get('S3Bucket') == BUCKET_MARKER_LOCAL if is_local_mount: # Mount or use a local folder lambda executors can reference # WARNING: this means we're pointing lambda_cwd to a local path in the user's # file system! We must ensure that there is no data loss (i.e., we must *not* add # this folder to TMP_FILES or similar). lambda_cwd = code['S3Key'] else: # Save the zip file to a temporary file that the lambda executors can reference zip_file_content = get_zip_bytes(code) if isinstance(zip_file_content, Response): return zip_file_content tmp_dir = '%s/zipfile.%s' % (config.TMP_FOLDER, short_uid()) mkdir(tmp_dir) tmp_file = '%s/%s' % (tmp_dir, LAMBDA_ZIP_FILE_NAME) save_file(tmp_file, zip_file_content) TMP_FILES.append(tmp_dir) lambda_cwd = tmp_dir # Set the appropriate lambda handler. lambda_handler = generic_handler if runtime == LAMBDA_RUNTIME_JAVA8: # The Lambda executors for Docker subclass LambdaExecutorContainers, # which runs Lambda in Docker by passing all *.jar files in the function # working directory as part of the classpath. Because of this, we need to # save the zip_file_content as a .jar here. if is_jar_archive(zip_file_content): jar_tmp_file = '{working_dir}/{file_name}'.format( working_dir=tmp_dir, file_name=LAMBDA_JAR_FILE_NAME) save_file(jar_tmp_file, zip_file_content) lambda_handler = get_java_handler(zip_file_content, handler_name, tmp_file) if isinstance(lambda_handler, Response): return lambda_handler else: handler_file = get_handler_file_from_name(handler_name, runtime=runtime) handler_function = get_handler_function_from_name(handler_name, runtime=runtime) if not is_local_mount: # Lambda code must be uploaded in Zip format if not is_zip_file(zip_file_content): raise Exception( 'Uploaded Lambda code for runtime ({}) is not in Zip format'.format(runtime)) unzip(tmp_file, lambda_cwd) main_file = '%s/%s' % (lambda_cwd, handler_file) if os.path.isfile(main_file): # make sure the file is actually readable, then read contents ensure_readable(main_file) with open(main_file, 'rb') as file_obj: zip_file_content = file_obj.read() else: # Raise an error if (1) this is not a local mount lambda, or (2) we're # running Lambdas locally (not in Docker), or (3) we're using remote Docker. # -> We do *not* want to raise an error if we're using local mount in non-remote Docker if not is_local_mount or not use_docker() or config.LAMBDA_REMOTE_DOCKER: file_list = run('ls -la %s' % lambda_cwd) LOG.debug('Lambda archive content:\n%s' % file_list) return error_response( 'Unable to find handler script in Lambda archive.', 400, error_type='ValidationError') if runtime.startswith('python') and not use_docker(): try: lambda_handler = exec_lambda_code( zip_file_content, handler_function=handler_function, lambda_cwd=lambda_cwd, lambda_env=lambda_environment) except Exception as e: raise Exception('Unable to get handler function from lambda code.', e) add_function_mapping(lambda_name, lambda_handler, lambda_cwd) return {'FunctionName': lambda_name}
def set_function_code(code, lambda_name): def generic_handler(event, context): raise Exception(( 'Unable to find executor for Lambda function "%s". ' + 'Note that Node.js and .NET Core Lambdas currently require LAMBDA_EXECUTOR=docker' ) % lambda_name) lambda_cwd = None arn = func_arn(lambda_name) runtime = arn_to_lambda[arn].runtime handler_name = arn_to_lambda.get(arn).handler lambda_environment = arn_to_lambda.get(arn).envvars if not handler_name: handler_name = LAMBDA_DEFAULT_HANDLER # Stop/remove any containers that this arn uses. LAMBDA_EXECUTOR.cleanup(arn) # Save the zip file to a temporary file that the lambda executors can reference. zip_file_content = get_zip_bytes(code) if isinstance(zip_file_content, Response): return zip_file_content tmp_dir = '%s/zipfile.%s' % (config.TMP_FOLDER, short_uid()) mkdir(tmp_dir) tmp_file = '%s/%s' % (tmp_dir, LAMBDA_ZIP_FILE_NAME) save_file(tmp_file, zip_file_content) TMP_FILES.append(tmp_dir) lambda_cwd = tmp_dir # Set the appropriate lambda handler. lambda_handler = generic_handler if runtime == LAMBDA_RUNTIME_JAVA8: # The Lambda executors for Docker subclass LambdaExecutorContainers, # which runs Lambda in Docker by passing all *.jar files in the function # working directory as part of the classpath. Because of this, we need to # save the zip_file_content as a .jar here. if is_jar_archive(zip_file_content): jar_tmp_file = '{working_dir}/{file_name}'.format( working_dir=tmp_dir, file_name=LAMBDA_JAR_FILE_NAME) save_file(jar_tmp_file, zip_file_content) lambda_handler = get_java_handler(zip_file_content, handler_name, tmp_file) if isinstance(lambda_handler, Response): return lambda_handler else: handler_file = get_handler_file_from_name(handler_name, runtime=runtime) handler_function = get_handler_function_from_name(handler_name, runtime=runtime) # Lambda code must be uploaded in the Zip format. if not is_zip_file(zip_file_content): raise Exception( 'Uploaded Lambda code for runtime ({}) is not in Zip format'. format(runtime)) unzip(tmp_file, tmp_dir) main_file = '%s/%s' % (tmp_dir, handler_file) if os.path.isfile(main_file): # make sure the file is actually readable, then read contents ensure_readable(main_file) with open(main_file, 'rb') as file_obj: zip_file_content = file_obj.read() else: file_list = run('ls -la %s' % tmp_dir) LOG.debug('Lambda archive content:\n%s' % file_list) return error_response( 'Unable to find handler script in Lambda archive.', 400, error_type='ValidationError') if runtime.startswith('python') and not use_docker(): try: lambda_handler = exec_lambda_code( zip_file_content, handler_function=handler_function, lambda_cwd=lambda_cwd, lambda_env=lambda_environment) except Exception as e: raise Exception( 'Unable to get handler function from lambda code.', e) add_function_mapping(lambda_name, lambda_handler, lambda_cwd) return {'FunctionName': lambda_name}
def set_function_code(code, lambda_name): def generic_handler(event, context): raise Exception(( 'Unable to find executor for Lambda function "%s". ' + 'Note that Node.js Lambdas currently require LAMBDA_EXECUTOR=docker' ) % lambda_name) lambda_handler = generic_handler lambda_cwd = None arn = func_arn(lambda_name) runtime = arn_to_lambda[arn].runtime handler_name = arn_to_lambda.get(arn).handler lambda_environment = arn_to_lambda.get(arn).envvars if not handler_name: handler_name = LAMBDA_DEFAULT_HANDLER handler_file = get_handler_file_from_name(handler_name, runtime=runtime) handler_function = get_handler_function_from_name(handler_name, runtime=runtime) # Stop/remove any containers that this arn uses. LAMBDA_EXECUTOR.cleanup(arn) if 'S3Bucket' in code: s3_client = aws_stack.connect_to_service('s3') bytes_io = BytesIO() try: s3_client.download_fileobj(code['S3Bucket'], code['S3Key'], bytes_io) zip_file_content = bytes_io.getvalue() except Exception as e: return error_response( 'Unable to fetch Lambda archive from S3: %s' % e, 404) elif 'ZipFile' in code: zip_file_content = code['ZipFile'] zip_file_content = base64.b64decode(zip_file_content) else: return error_response('No valid Lambda archive specified.', 400) # save tmp file tmp_dir = '%s/zipfile.%s' % (config.TMP_FOLDER, short_uid()) run('mkdir -p %s' % tmp_dir) tmp_file = '%s/%s' % (tmp_dir, LAMBDA_ZIP_FILE_NAME) save_file(tmp_file, zip_file_content) TMP_FILES.append(tmp_dir) lambda_cwd = tmp_dir # check if this is a ZIP file is_zip = is_zip_file(zip_file_content) if is_zip: unzip(tmp_file, tmp_dir) main_file = '%s/%s' % (tmp_dir, handler_file) if not os.path.isfile(main_file): # check if this is a zip file that contains a single JAR file jar_files = glob.glob('%s/*.jar' % tmp_dir) if len(jar_files) == 1: main_file = jar_files[0] if os.path.isfile(main_file): # make sure the file is actually readable, then read contents ensure_readable(main_file) with open(main_file, 'rb') as file_obj: zip_file_content = file_obj.read() else: file_list = run('ls -la %s' % tmp_dir) LOG.debug('Lambda archive content:\n%s' % file_list) return error_response( 'Unable to find handler script in Lambda archive.', 400, error_type='ValidationError') # it could be a JAR file (regardless of whether wrapped in a ZIP file or not) is_jar = is_jar_archive(zip_file_content) if is_jar: def execute(event, context): result, log_output = lambda_executors.EXECUTOR_LOCAL.execute_java_lambda( event, context, handler=arn_to_lambda[arn].handler, main_file=main_file) return result lambda_handler = execute elif runtime.startswith('python') and not use_docker(): try: lambda_handler = exec_lambda_code( zip_file_content, handler_function=handler_function, lambda_cwd=lambda_cwd, lambda_env=lambda_environment) except Exception as e: raise Exception('Unable to get handler function from lambda code.', e) if not is_zip and not is_jar: raise Exception('Uploaded Lambda code is neither a ZIP nor JAR file.') add_function_mapping(lambda_name, lambda_handler, lambda_cwd) return {'FunctionName': lambda_name}
def set_function_code(code, lambda_name, lambda_cwd=None): def generic_handler(event, context): raise ClientError(('Unable to find executor for Lambda function "%s". Note that ' + 'Node.js, Golang, and .Net Core Lambdas currently require LAMBDA_EXECUTOR=docker') % lambda_name) arn = func_arn(lambda_name) lambda_details = arn_to_lambda[arn] runtime = lambda_details.runtime lambda_environment = lambda_details.envvars handler_name = lambda_details.handler or LAMBDA_DEFAULT_HANDLER code_passed = code code = code or lambda_details.code is_local_mount = code.get('S3Bucket') == BUCKET_MARKER_LOCAL zip_file_content = None if code_passed: lambda_cwd = lambda_cwd or set_archive_code(code_passed, lambda_name) if not is_local_mount: # Save the zip file to a temporary file that the lambda executors can reference zip_file_content = get_zip_bytes(code_passed) else: lambda_cwd = lambda_cwd or lambda_details.cwd # get local lambda working directory tmp_file = '%s/%s' % (lambda_cwd, LAMBDA_ZIP_FILE_NAME) if not zip_file_content: zip_file_content = load_file(tmp_file, mode='rb') # Set the appropriate lambda handler. lambda_handler = generic_handler if runtime == LAMBDA_RUNTIME_JAVA8: # The Lambda executors for Docker subclass LambdaExecutorContainers, # which runs Lambda in Docker by passing all *.jar files in the function # working directory as part of the classpath. Because of this, we need to # save the zip_file_content as a .jar here. lambda_handler, zip_file_content = get_java_handler(zip_file_content, handler_name, tmp_file) if is_jar_archive(zip_file_content): jar_tmp_file = '{working_dir}/{file_name}'.format( working_dir=lambda_cwd, file_name=LAMBDA_JAR_FILE_NAME) save_file(jar_tmp_file, zip_file_content) else: handler_file = get_handler_file_from_name(handler_name, runtime=runtime) handler_function = get_handler_function_from_name(handler_name, runtime=runtime) if not is_local_mount: # Lambda code must be uploaded in Zip format if not is_zip_file(zip_file_content): raise ClientError( 'Uploaded Lambda code for runtime ({}) is not in Zip format'.format(runtime)) unzip(tmp_file, lambda_cwd) main_file = '%s/%s' % (lambda_cwd, handler_file) if not os.path.exists(main_file): # Raise an error if (1) this is not a local mount lambda, or (2) we're # running Lambdas locally (not in Docker), or (3) we're using remote Docker. # -> We do *not* want to raise an error if we're using local mount in non-remote Docker if not is_local_mount or not use_docker() or config.LAMBDA_REMOTE_DOCKER: file_list = run('cd "%s"; du -d 3 .' % lambda_cwd) config_debug = ('Config for local mount, docker, remote: "%s", "%s", "%s"' % (is_local_mount, use_docker(), config.LAMBDA_REMOTE_DOCKER)) LOG.debug('Lambda archive content:\n%s' % file_list) raise ClientError(error_response( 'Unable to find handler script in Lambda archive. %s' % config_debug, 400, error_type='ValidationError')) if runtime.startswith('python') and not use_docker(): try: # make sure the file is actually readable, then read contents ensure_readable(main_file) zip_file_content = load_file(main_file, mode='rb') # extract handler lambda_handler = exec_lambda_code( zip_file_content, handler_function=handler_function, lambda_cwd=lambda_cwd, lambda_env=lambda_environment) except Exception as e: raise ClientError('Unable to get handler function from lambda code.', e) add_function_mapping(lambda_name, lambda_handler, lambda_cwd) return {'FunctionName': lambda_name}
def set_function_code(code, lambda_name): def generic_handler(event, context): raise Exception(( 'Unable to find executor for Lambda function "%s". ' + 'Note that Node.js Lambdas currently require LAMBDA_EXECUTOR=docker' ) % lambda_name) lambda_handler = generic_handler lambda_cwd = None arn = func_arn(lambda_name) runtime = arn_to_lambda[arn].runtime handler_name = arn_to_lambda.get(arn).handler lambda_environment = arn_to_lambda.get(arn).envvars if not handler_name: handler_name = LAMBDA_DEFAULT_HANDLER handler_file = get_handler_file_from_name(handler_name, runtime=runtime) handler_function = get_handler_function_from_name(handler_name, runtime=runtime) if 'S3Bucket' in code: s3_client = aws_stack.connect_to_service('s3') bytes_io = BytesIO() try: s3_client.download_fileobj(code['S3Bucket'], code['S3Key'], bytes_io) zip_file_content = bytes_io.getvalue() except Exception as e: return error_response( 'Unable to fetch Lambda archive from S3: %s' % e, 404) elif 'ZipFile' in code: zip_file_content = code['ZipFile'] zip_file_content = base64.b64decode(zip_file_content) else: return error_response('No valid Lambda archive specified.', 400) # save tmp file tmp_dir = '%s/zipfile.%s' % (config.TMP_FOLDER, short_uid()) run('mkdir -p %s' % tmp_dir) tmp_file = '%s/%s' % (tmp_dir, LAMBDA_ZIP_FILE_NAME) save_file(tmp_file, zip_file_content) TMP_FILES.append(tmp_dir) lambda_cwd = tmp_dir # check if this is a ZIP file is_zip = is_zip_file(zip_file_content) if is_zip: unzip(tmp_file, tmp_dir) main_file = '%s/%s' % (tmp_dir, handler_file) if not os.path.isfile(main_file): # check if this is a zip file that contains a single JAR file jar_files = glob.glob('%s/*.jar' % tmp_dir) if len(jar_files) == 1: main_file = jar_files[0] if os.path.isfile(main_file): with open(main_file, 'rb') as file_obj: zip_file_content = file_obj.read() else: file_list = run('ls -la %s' % tmp_dir) LOG.debug('Lambda archive content:\n%s' % file_list) return error_response( 'Unable to find handler script in Lambda archive.', 400, error_type='ValidationError') # it could be a JAR file (regardless of whether wrapped in a ZIP file or not) is_jar = is_jar_archive(zip_file_content) if is_jar: def execute(event, context): event_file = EVENT_FILE_PATTERN.replace('*', short_uid()) save_file(event_file, json.dumps(event)) TMP_FILES.append(event_file) class_name = arn_to_lambda[arn].handler.split('::')[0] classpath = '%s:%s' % (LAMBDA_EXECUTOR_JAR, main_file) cmd = 'java -cp %s %s %s %s' % (classpath, LAMBDA_EXECUTOR_CLASS, class_name, event_file) async = False # flip async flag depending on origin if 'Records' in event: # TODO: add more event supporting async lambda execution if 'Sns' in event['Records'][0]: async = True result, log_output = run_lambda_executor(cmd, async=async) LOG.info('Lambda output: %s' % log_output.replace('\n', '\n> ')) return result
def set_function_code(code, lambda_name): def generic_handler(event, context): raise Exception(('Unable to find executor for Lambda function "%s". ' + 'Note that Node.js and .NET Core Lambdas currently require LAMBDA_EXECUTOR=docker') % lambda_name) lambda_handler = generic_handler lambda_cwd = None arn = func_arn(lambda_name) runtime = arn_to_lambda[arn].runtime handler_name = arn_to_lambda.get(arn).handler lambda_environment = arn_to_lambda.get(arn).envvars if not handler_name: handler_name = LAMBDA_DEFAULT_HANDLER handler_file = get_handler_file_from_name(handler_name, runtime=runtime) handler_function = get_handler_function_from_name(handler_name, runtime=runtime) # Stop/remove any containers that this arn uses. LAMBDA_EXECUTOR.cleanup(arn) if 'S3Bucket' in code: s3_client = aws_stack.connect_to_service('s3') bytes_io = BytesIO() try: s3_client.download_fileobj(code['S3Bucket'], code['S3Key'], bytes_io) zip_file_content = bytes_io.getvalue() except Exception as e: return error_response('Unable to fetch Lambda archive from S3: %s' % e, 404) elif 'ZipFile' in code: zip_file_content = code['ZipFile'] zip_file_content = base64.b64decode(zip_file_content) else: return error_response('No valid Lambda archive specified.', 400) # save tmp file tmp_dir = '%s/zipfile.%s' % (config.TMP_FOLDER, short_uid()) mkdir(tmp_dir) tmp_file = '%s/%s' % (tmp_dir, LAMBDA_ZIP_FILE_NAME) save_file(tmp_file, zip_file_content) TMP_FILES.append(tmp_dir) lambda_cwd = tmp_dir # check if this is a ZIP file is_zip = is_zip_file(zip_file_content) if is_zip: unzip(tmp_file, tmp_dir) main_file = '%s/%s' % (tmp_dir, handler_file) if not os.path.isfile(main_file): # check if this is a zip file that contains a single JAR file jar_files = glob.glob('%s/*.jar' % tmp_dir) if len(jar_files) == 1: main_file = jar_files[0] if os.path.isfile(main_file): # make sure the file is actually readable, then read contents ensure_readable(main_file) with open(main_file, 'rb') as file_obj: zip_file_content = file_obj.read() else: file_list = run('ls -la %s' % tmp_dir) LOG.debug('Lambda archive content:\n%s' % file_list) return error_response('Unable to find handler script in Lambda archive.', 400, error_type='ValidationError') # it could be a JAR file (regardless of whether wrapped in a ZIP file or not) is_jar = is_jar_archive(zip_file_content) if is_jar: def execute(event, context): result, log_output = lambda_executors.EXECUTOR_LOCAL.execute_java_lambda(event, context, handler=arn_to_lambda[arn].handler, main_file=main_file) return result lambda_handler = execute elif runtime.startswith('python') and not use_docker(): try: lambda_handler = exec_lambda_code(zip_file_content, handler_function=handler_function, lambda_cwd=lambda_cwd, lambda_env=lambda_environment) except Exception as e: raise Exception('Unable to get handler function from lambda code.', e) if not is_zip and not is_jar: raise Exception('Uploaded Lambda code is neither a ZIP nor JAR file.') add_function_mapping(lambda_name, lambda_handler, lambda_cwd) return {'FunctionName': lambda_name}