def test_create_function(make_stubber, make_unique_name, error_code): lambda_client = boto3.client('lambda') lambda_stubber = make_stubber(lambda_client) wrapper = LambdaWrapper(lambda_client, None) func_name = make_unique_name('func-') handler_name = make_unique_name('handler-') iam_role = unittest.mock.MagicMock(arn='arn:aws:iam:::role/test-role') test_package = 'test-package' func_arn = f'arn:aws:lambda:::function/{func_name}' lambda_stubber.stub_create_function(func_name, func_arn, iam_role.arn, handler_name, test_package, error_code=error_code) if error_code is None: lambda_stubber.stub_get_function(func_name, 'Active') if error_code is None: got_arn = wrapper.create_function(func_name, handler_name, iam_role, test_package) assert got_arn == func_arn else: with pytest.raises(ClientError) as exc_info: wrapper.create_function(func_name, handler_name, iam_role, test_package) assert exc_info.value.response['Error']['Code'] == error_code
def test_update_function_configuration(make_stubber, error_code): lambda_client = boto3.client('lambda') lambda_stubber = make_stubber(lambda_client) wrapper = LambdaWrapper(lambda_client, None) func_name = 'test-func_name' env_vars = {'test-key': 'test-val'} lambda_stubber.stub_update_function_configuration(func_name, env_vars, error_code=error_code) if error_code is None: got_response = wrapper.update_function_configuration( func_name, env_vars) assert got_response else: with pytest.raises(ClientError) as exc_info: wrapper.update_function_configuration(func_name, env_vars) assert exc_info.value.response['Error']['Code'] == error_code
def test_update_function_code(make_stubber, error_code): lambda_client = boto3.client('lambda') lambda_stubber = make_stubber(lambda_client) wrapper = LambdaWrapper(lambda_client, None) func_name = 'test-func_name' package = 'test-package' update_status = 'InProgress' lambda_stubber.stub_update_function_code(func_name, update_status, package=package, error_code=error_code) if error_code is None: got_response = wrapper.update_function_code(func_name, package) assert got_response['LastUpdateStatus'] == update_status else: with pytest.raises(ClientError) as exc_info: wrapper.update_function_code(func_name, package) assert exc_info.value.response['Error']['Code'] == error_code
def test_invoke_function(make_stubber, make_unique_name, error_code): lambda_client = boto3.client('lambda') lambda_stubber = make_stubber(lambda_client) wrapper = LambdaWrapper(lambda_client, None) func_name = make_unique_name('func-') func_params = {'param1': 'test', 'param2': 35} response_payload = 'ahoy there' lambda_stubber.stub_invoke(func_name, json.dumps(func_params), response_payload, log_type='None', error_code=error_code) if error_code is None: response = wrapper.invoke_function(func_name, func_params) assert response['Payload'] == response_payload else: with pytest.raises(ClientError) as exc_info: wrapper.invoke_function(func_name, func_params) assert exc_info.value.response['Error']['Code'] == error_code
def test_create_iam_role_for_lambda(make_stubber, make_unique_name, stub_runner, error_code, stop_on_method): iam_resource = boto3.resource('iam') iam_stubber = make_stubber(iam_resource.meta.client) wrapper = LambdaWrapper(None, iam_resource) role_name = make_unique_name('role-') with stub_runner(error_code, stop_on_method) as runner: runner.add(iam_stubber.stub_get_role, role_name, error_code='NoSuchEntity') runner.add(iam_stubber.stub_create_role, role_name) runner.add( iam_stubber.stub_attach_role_policy, role_name, 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole') if error_code is None: got_role, got_created = wrapper.create_iam_role_for_lambda(role_name) assert got_role.name == role_name assert got_created else: with pytest.raises(ClientError) as exc_info: wrapper.create_iam_role_for_lambda(role_name) assert exc_info.value.response['Error']['Code'] == error_code
def test_get_function(make_stubber, error_code): lambda_client = boto3.client('lambda') lambda_stubber = make_stubber(lambda_client) wrapper = LambdaWrapper(lambda_client, None) func_name = 'test-func_name' lambda_stubber.stub_get_function(func_name, error_code=error_code) if error_code in (None, 'ResourceNotFoundException'): wrapper.get_function(func_name) else: with pytest.raises(ClientError) as exc_info: wrapper.get_function(func_name) assert exc_info.value.response['Error']['Code'] == error_code
def test_delete_function(make_stubber, make_unique_name, error_code): lambda_client = boto3.client('lambda') lambda_stubber = make_stubber(lambda_client) wrapper = LambdaWrapper(lambda_client, None) func_name = make_unique_name('func-') lambda_stubber.stub_delete_function(func_name, error_code=error_code) if error_code is None: wrapper.delete_function(func_name) else: with pytest.raises(ClientError) as exc_info: wrapper.delete_function(func_name) assert exc_info.value.response['Error']['Code'] == error_code
def test_list_functions(make_stubber, error_code): lambda_client = boto3.client('lambda') lambda_stubber = make_stubber(lambda_client) wrapper = LambdaWrapper(lambda_client, None) funcs = [{ 'FunctionName': f'test-func-{index}', 'Description': f'test description {index}', 'Runtime': f'test-runtime-{index}', 'Handler': f'test-handler-{index}' } for index in range(3)] lambda_stubber.stub_list_functions(funcs, error_code=error_code) if error_code is None: wrapper.list_functions() else: with pytest.raises(ClientError) as exc_info: wrapper.list_functions() assert exc_info.value.response['Error']['Code'] == error_code
def run_scenario(lambda_client, iam_resource, basic_file, calculator_file, lambda_name): """ Runs the scenario. :param lambda_client: A Boto3 Lambda client. :param iam_resource: A Boto3 IAM resource. :param basic_file: The name of the file that contains the basic Lambda handler. :param calculator_file: The name of the file that contains the calculator Lambda handler. :param lambda_name: The name to give resources created for the scenario, such as the IAM role and the Lambda function. """ logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') print('-' * 88) print("Welcome to the AWS Lambda getting started with functions demo.") print('-' * 88) wrapper = LambdaWrapper(lambda_client, iam_resource) print("Checking for IAM role for Lambda...") iam_role, should_wait = wrapper.create_iam_role_for_lambda(lambda_name) if should_wait: logger.info("Giving AWS time to create resources...") wait(10) print(f"Looking for function {lambda_name}...") function = wrapper.get_function(lambda_name) if function is None: print("Zipping the Python script into a deployment package...") deployment_package = wrapper.create_deployment_package( basic_file, f"{lambda_name}.py") print(f"...and creating the {lambda_name} Lambda function.") wrapper.create_function(lambda_name, f'{lambda_name}.lambda_handler', iam_role, deployment_package) else: print(f"Function {lambda_name} already exists.") print('-' * 88) print(f"Let's invoke {lambda_name}. This function increments a number.") action_params = { 'action': 'increment', 'number': q.ask("Give me a number to increment: ", q.is_int) } print(f"Invoking {lambda_name}...") response = wrapper.invoke_function(lambda_name, action_params) print(f"Incrementing {action_params['number']} resulted in " f"{json.load(response['Payload'])}") print('-' * 88) print(f"Let's update the function to an arithmetic calculator.") q.ask("Press Enter when you're ready.") print("Creating a new deployment package...") deployment_package = wrapper.create_deployment_package( calculator_file, f"{lambda_name}.py") print(f"...and updating the {lambda_name} Lambda function.") update_waiter = UpdateFunctionWaiter(lambda_client) wrapper.update_function_code(lambda_name, deployment_package) update_waiter.wait(lambda_name) print( f"This function uses an environment variable to control logging level." ) print(f"Let's set it to DEBUG to get the most logging.") wrapper.update_function_configuration( lambda_name, {'LOG_LEVEL': logging.getLevelName(logging.DEBUG)}) actions = ['plus', 'minus', 'times', 'divided-by'] want_invoke = True while want_invoke: print(f"Let's invoke {lambda_name}. You can invoke these actions:") for index, action in enumerate(actions): print(f"{index + 1}: {action}") action_params = {} action_index = q.ask( "Enter the number of the action you want to take: ", q.is_int, q.in_range(1, len(actions))) action_params['action'] = actions[action_index - 1] print(f"You've chosen to invoke 'x {action_params['action']} y'.") action_params['x'] = q.ask("Enter a value for x: ", q.is_int) action_params['y'] = q.ask("Enter a value for y: ", q.is_int) print(f"Invoking {lambda_name}...") response = wrapper.invoke_function(lambda_name, action_params, True) print( f"Calculating {action_params['x']} {action_params['action']} {action_params['y']} " f"resulted in {json.load(response['Payload'])}") q.ask("Press Enter to see the logs from the call.") print(base64.b64decode(response['LogResult']).decode()) want_invoke = q.ask("That was fun. Shall we do it again? (y/n) ", q.is_yesno) print('-' * 88) if q.ask("Do you want to list all of the functions in your account? (y/n) " ): wrapper.list_functions() print('-' * 88) if q.ask("Ready to delete the function and role? (y/n) ", q.is_yesno): for policy in iam_role.attached_policies.all(): policy.detach_role(RoleName=iam_role.name) iam_role.delete() print(f"Deleted role {lambda_name}.") wrapper.delete_function(lambda_name) print(f"Deleted function {lambda_name}.") print("\nThanks for watching!") print('-' * 88)
def test_create_lambda_deployment_package(monkeypatch): monkeypatch.setattr(zipfile.ZipFile, 'write', lambda x, y, z: None) wrapper = LambdaWrapper(None, None) got_package = wrapper.create_deployment_package('test-file', 'other-file') assert got_package is not None