def test_get_awsclient_args_missing_methodname(): """Missing methodName raises.""" context = Context({ 'k1': 'v1', 'awsClientIn': { 'serviceName': 'service name', 'arbKey': 'arb_value' } }) with pytest.raises(KeyNotInContextError) as err_info: contextargs.get_awsclient_args(context['awsClientIn'], 'pypyraws.steps.client') assert str(err_info.value) == ("awsClientIn missing required key for " "pypyraws.steps.client: 'methodName'")
def test_get_awsclient_args_methodname_empty(): """Whitespace serviceName raises.""" context = Context({ 'k1': 'v1', 'awsClientIn': { 'serviceName': 'service name', 'methodName': ' ', 'arbKey': 'arb_value' } }) with pytest.raises(KeyInContextHasNoValueError) as err_info: contextargs.get_awsclient_args(context['awsClientIn'], 'pypyraws.steps.client') assert str(err_info.value) == ("methodName required in awsClientIn " "for pypyraws.steps.client")
def test_get_awsclient_args_missing_awsclientin(): """Missing awsClientIn raises""" context = Context({'k1': 'v1'}) with pytest.raises(KeyNotInContextError) as err_info: client_in, service_name, method_name = contextargs.get_awsclient_args( context, 'pypyraws.steps.client') assert repr(err_info.value) == ( "KeyNotInContextError(\'awsClientIn not found in the pypyr " "context.',)")
def run_step(context): """Execute any low-level boto3 client method. All of the awsClientIn descendant values support {key} string interpolation. Args: context: Dictionary. Mandatory. Requires the following context keys in context: - awsClientIn. dict. mandatory. Contains keys: The awsClientIn dictionary should contain: - serviceName: mandatory. String for service name. Available services here: http://boto3.readthedocs.io/en/latest/reference/services/ - methodName: mandatory. String. Name of method to execute. - clientArgs: optional. Dict. kwargs for the boto client ctor. - methodArgs: optional. Dict. kwargs for the client method call Returns: None. Although there is no return, this does add awsClientOut to context. Adds key to context for response from aws client: - awsClientOut. Dictionary containing the full aws response. Raises: botocore.exceptions.ClientError: Anything inside boto went wrong. pypyr.errors.KeyNotInContextError: awsClientIn missing in context. pypyr.errors.KeyInContextHasNoValueError: awsClientIn exists but is None. """ logger.debug("started") client_in, service_name, method_name = contextargs.get_awsclient_args( context, __name__) client_args = contextargs.get_formatted_iterable(input_dict=client_in, field_name='clientArgs', context=context) method_args = contextargs.get_formatted_iterable(input_dict=client_in, field_name='methodArgs', context=context) context['awsClientOut'] = pypyraws.aws.service.operation_exec( service_name=context.get_formatted_string(service_name), method_name=context.get_formatted_string(method_name), client_args=client_args, operation_args=method_args) logger.debug("aws response in context['awsClientOut']") logger.info(f"Executed {method_name} on aws {service_name}.") logger.debug("done")
def test_get_awsclient_args_missing_servicename(): """Missing serviceName raises""" context = Context({ 'k1': 'v1', 'awsClientIn': { 'methodName': 'method_name', 'arbKey': 'arb_value' }}) with pytest.raises(KeyNotInContextError) as err_info: client_in, service_name, method_name = contextargs.get_awsclient_args( context, 'pypyraws.steps.client') assert repr(err_info.value) == ( "KeyNotInContextError(\"awsClientIn missing required key for " "pypyraws.steps.client: 'serviceName'\",)")
def test_get_awsclient_args_servicename_empty(): """Empty serviceName raises""" context = Context({ 'k1': 'v1', 'awsClientIn': { 'serviceName': '', 'methodName': 'method_name', 'arbKey': 'arb_value' }}) with pytest.raises(KeyInContextHasNoValueError) as err_info: client_in, service_name, method_name = contextargs.get_awsclient_args( context, 'pypyraws.steps.client') assert repr(err_info.value) == ( "KeyInContextHasNoValueError('serviceName required in awsClientIn " "for pypyraws.steps.client',)")
def test_get_awsclient_args_pass(): """get_awsclient_args pass.""" context = Context({ 'k1': 'v1', 'awsClientIn': { 'serviceName': 'service name', 'methodName': 'method_name', 'arbKey': 'arb_value' } }) (service_name, method_name, client_args, method_args) = contextargs.get_awsclient_args(context['awsClientIn'], 'pypyraws.steps.client') assert service_name == 'service name' assert method_name == 'method_name' assert client_args is None assert method_args is None
def test_get_awsclient_args_pass(): """get_awsclient_args pass""" context = Context({ 'k1': 'v1', 'awsClientIn': { 'serviceName': 'service name', 'methodName': 'method_name', 'arbKey': 'arb_value' }}) client_in, service_name, method_name = contextargs.get_awsclient_args( context, 'pypyraws.steps.client') assert client_in == { 'serviceName': 'service name', 'methodName': 'method_name', 'arbKey': 'arb_value' } assert service_name == 'service name' assert method_name == 'method_name'
def run_step(context): """Custom waiter for any aws client operation. All of the awsWaitFor descendant values support {key} string interpolation, except waitForField. Args: context: Dictionary. Mandatory. Requires the following context keys in context: - awsWaitFor. dict. mandatory. Contains keys: - awsClientIn. dict. mandatory. This is the same as for the pypyraws.steps.client in step. Contains keys: - serviceName: mandatory. String for service name. Available services here: http://boto3.readthedocs.io/en/latest/reference/services/ - methodName: mandatory. String. Name of method to execute. - clientArgs: optional. Dict. kwargs for the boto client ctor. - methodArgs: optional. Dict. kwargs for the client method call - waitForField: mandatory. string. format expression for field name to check in awsClient response. - toBe: mandatory. string. string. Stop waiting when waitForField equals this value. - pollInterval: optional. int. In seconds. Default to 30. - maxAttempts: optional. int. Default 10. - errorOnWaitTimeout: optional. Default True. Throws error if maxAttempts exhausted without reaching toBe value. If false, step completes without raising error. Returns: None Adds key to context: - awsWaitForTimedOut: bool. Adds key with value True if errorOnWaitTimeout=False and max_attempts exhausted without reaching toBe. If steps completes successfully and waitForField's value becomes toBe, awsWaitForTimedOut == False. Raises: pypyr.errors.KeyNotInContextError: awsWaitFor missing in context. pypyr.errors.KeyInContextHasNoValueError: awsWaitFor exists but is None. pypyraws.errors.WaitTimeOut: maxAttempts exceeded without waitForField changing to toBe. """ logger.debug("started") context.assert_key_has_value('awsWaitFor', __name__) wait_for = context['awsWaitFor'] client_in, service_name, method_name = contextargs.get_awsclient_args( wait_for, __name__) service_name = context.get_formatted_string(service_name) method_name = context.get_formatted_string(method_name) client_args = contextargs.get_formatted_iterable(input_dict=client_in, field_name='clientArgs', context=context) method_args = contextargs.get_formatted_iterable(input_dict=client_in, field_name='methodArgs', context=context) (wait_for_field, to_be, poll_interval, max_attempts, error_on_wait_timeout) = get_poll_args(wait_for, context) wait_response = wait_until_true( interval=poll_interval, max_attempts=max_attempts)(execute_aws_client_method)( service_name=service_name, method_name=method_name, client_args=client_args, method_args=method_args, wait_for_field=wait_for_field, to_be=to_be) if wait_response: context['awsWaitForTimedOut'] = False logger.info(f"aws {service_name} {method_name} returned {to_be}. " "Pipeline will now continue.") else: if error_on_wait_timeout: context['awsWaitForTimedOut'] = True logger.error(f"aws {service_name} {method_name} did not return " f"{to_be} within {max_attempts}. errorOnWaitTimeout " "is True, throwing error") raise WaitTimeOut(f"aws {service_name} {method_name} did not " f"return {to_be} within {max_attempts} retries.") else: context['awsWaitForTimedOut'] = True logger.warn(f"aws {service_name} {method_name} did NOT return " f" {to_be}. errorOnWaitTimeout is False, so pipeline " "will proceed to the next step anyway.") logger.debug("done")