def do_forward_request_network(port, method, path, data, headers, target_url=None): # TODO: enable per-service endpoints, to allow deploying in distributed settings target_url = target_url or "%s://%s:%s" % (get_service_protocol(), LOCALHOST, port) url = "%s%s" % (target_url, path) response = requests.request( method, url, data=data, headers=headers, verify=False, stream=True, allow_redirects=False ) return response
def auth_keys_from_connection(connection: Dict): headers = {} auth_type = connection.get("AuthorizationType").upper() auth_parameters = connection.get("AuthParameters") if auth_type == AUTH_BASIC: basic_auth_parameters = auth_parameters.get("BasicAuthParameters", {}) username = basic_auth_parameters.get("Username", "") password = basic_auth_parameters.get("Password", "") headers.update( {"authorization": "Basic {}:{}".format(username, password)}) if auth_type == AUTH_API_KEY: api_key_parameters = auth_parameters.get("ApiKeyAuthParameters", {}) api_key_name = api_key_parameters.get("ApiKeyName", "") api_key_value = api_key_parameters.get("ApiKeyValue", "") headers.update({api_key_name: api_key_value}) if auth_type == AUTH_OAUTH: oauth_parameters = auth_parameters.get("OAuthParameters", {}) oauth_method = oauth_parameters.get("HttpMethod") oauth_endpoint = oauth_parameters.get("AuthorizationEndpoint", "") query_object = list_of_parameters_to_object( oauth_parameters.get("QueryStringParameters", [])) oauth_endpoint = add_query_params_to_url(oauth_endpoint, query_object) client_parameters = oauth_parameters.get("ClientParameters", {}) client_id = client_parameters.get("ClientID", "") client_secret = client_parameters.get("ClientSecret", "") oauth_body = list_of_parameters_to_object( oauth_parameters.get("BodyParameters", [])) oauth_body.update({ "client_id": client_id, "client_secret": client_secret }) oauth_header = list_of_parameters_to_object( oauth_parameters.get("HeaderParameters", [])) oauth_result = requests.request( method=oauth_method, url=oauth_endpoint, data=json.dumps(oauth_body), headers=oauth_header, ) oauth_data = json.loads(oauth_result.text) token_type = oauth_data.get("token_type", "") access_token = oauth_data.get("access_token", "") auth_header = "{} {}".format(token_type, access_token) headers.update({"authorization": auth_header}) return headers
def send_event_to_api_destination(target_arn, event): """Send an event to an EventBridge API destination See https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-api-destinations.html""" # ARN format: ...:api-destination/{name}/{uuid} region = target_arn.split(":")[3] api_destination_name = target_arn.split(":")[-1].split("/")[1] events_client = connect_to_service("events", region_name=region) destination = events_client.describe_api_destination( Name=api_destination_name) # get destination endpoint details method = destination.get("HttpMethod", "GET") endpoint = destination.get("InvocationEndpoint") state = destination.get("ApiDestinationState") or "ACTIVE" LOG.debug('Calling EventBridge API destination (state "%s"): %s %s' % (state, method, endpoint)) headers = { # default headers AWS sends with every api destination call "User-Agent": "Amazon/EventBridge/ApiDestinations", "Content-Type": "application/json; charset=utf-8", "Range": "bytes=0-1048575", "Accept-Encoding": "gzip,deflate", "Connection": "close", } # add auth headers for target destination add_api_destination_authorization(destination, headers, event) # TODO: consider option to disable the actual network call to avoid unintended side effects # TODO: InvocationRateLimitPerSecond (needs some form of thread-safety, scoped to the api destination) result = requests.request(method=method, url=endpoint, data=json.dumps(event or {}), headers=headers) if result.status_code >= 400: LOG.debug("Received code %s forwarding events: %s %s" % (result.status_code, method, endpoint)) if result.status_code == 429 or 500 <= result.status_code <= 600: pass # TODO: retry logic (only retry on 429 and 5xx response status)
def send_event_to_target(arn, event, target_attributes=None, asynchronous=True): region = arn.split(':')[3] if ':lambda:' in arn: from localstack.services.awslambda import lambda_api lambda_api.run_lambda(func_arn=arn, event=event, context={}, asynchronous=asynchronous) elif ':sns:' in arn: sns_client = connect_to_service('sns', region_name=region) sns_client.publish(TopicArn=arn, Message=json.dumps(event)) elif ':sqs:' in arn: sqs_client = connect_to_service('sqs', region_name=region) queue_url = get_sqs_queue_url(arn) msg_group_id = dict_utils.get_safe(target_attributes, '$.SqsParameters.MessageGroupId') kwargs = {'MessageGroupId': msg_group_id} if msg_group_id else {} sqs_client.send_message(QueueUrl=queue_url, MessageBody=json.dumps(event), **kwargs) elif ':states:' in arn: stepfunctions_client = connect_to_service('stepfunctions', region_name=region) stepfunctions_client.start_execution(stateMachineArn=arn, input=json.dumps(event)) elif ':firehose:' in arn: delivery_stream_name = firehose_name(arn) firehose_client = connect_to_service('firehose', region_name=region) firehose_client.put_record( DeliveryStreamName=delivery_stream_name, Record={'Data': to_bytes(json.dumps(event))}) elif ':events:' in arn: events_client = connect_to_service('events', region_name=region) target_name = arn.split(':')[-1].split('/')[-1] if ':destination/' in arn: destination = events_client.describe_api_destination(Name=target_name) method = destination.get('HttpMethod', 'GET') endpoint = destination.get('InvocationEndpoint') state = destination.get('ApiDestinationState') or 'ACTIVE' LOG.debug('Calling EventBridge API destination (state "%s"): %s %s' % (state, method, endpoint)) result = requests.request(method=method, url=endpoint, data=json.dumps(event or {})) if result.status_code >= 400: LOG.debug('Received code %s forwarding events: %s %s' % (result.status_code, method, endpoint)) else: events_client.put_events( Entries=[{ 'EventBusName': target_name, 'Source': event.get('source'), 'DetailType': event.get('detail-type'), 'Detail': event.get('detail') }] ) elif ':kinesis:' in arn: partition_key_path = dict_utils.get_safe( target_attributes, '$.KinesisParameters.PartitionKeyPath', default_value='$.id' ) stream_name = arn.split('/')[-1] partition_key = dict_utils.get_safe(event, partition_key_path, event['id']) kinesis_client = connect_to_service('kinesis', region_name=region) kinesis_client.put_record( StreamName=stream_name, Data=to_bytes(json.dumps(event)), PartitionKey=partition_key ) else: LOG.warning('Unsupported Events rule target ARN: "%s"' % arn)
def send_event_to_target(target_arn, event, target_attributes=None, asynchronous=True): region = target_arn.split(":")[3] if ":lambda:" in target_arn: from localstack.services.awslambda import lambda_api lambda_api.run_lambda( func_arn=target_arn, event=event, context={}, asynchronous=asynchronous ) elif ":sns:" in target_arn: sns_client = connect_to_service("sns", region_name=region) sns_client.publish(TopicArn=target_arn, Message=json.dumps(event)) elif ":sqs:" in target_arn: sqs_client = connect_to_service("sqs", region_name=region) queue_url = get_sqs_queue_url(target_arn) msg_group_id = dict_utils.get_safe(target_attributes, "$.SqsParameters.MessageGroupId") kwargs = {"MessageGroupId": msg_group_id} if msg_group_id else {} sqs_client.send_message(QueueUrl=queue_url, MessageBody=json.dumps(event), **kwargs) elif ":states:" in target_arn: stepfunctions_client = connect_to_service("stepfunctions", region_name=region) stepfunctions_client.start_execution(stateMachineArn=target_arn, input=json.dumps(event)) elif ":firehose:" in target_arn: delivery_stream_name = firehose_name(target_arn) firehose_client = connect_to_service("firehose", region_name=region) firehose_client.put_record( DeliveryStreamName=delivery_stream_name, Record={"Data": to_bytes(json.dumps(event))}, ) elif ":events:" in target_arn: events_client = connect_to_service("events", region_name=region) if ":api-destination/" in target_arn: # API destination support # see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-api-destinations.html api_destination_name = target_arn.split(":")[-1].split("/")[ 1 ] # ...:api-destination/{name}/{uuid} destination = events_client.describe_api_destination(Name=api_destination_name) method = destination.get("HttpMethod", "GET") endpoint = destination.get("InvocationEndpoint") state = destination.get("ApiDestinationState") or "ACTIVE" LOG.debug( 'Calling EventBridge API destination (state "%s"): %s %s' % (state, method, endpoint) ) # TODO: support connection/auth (BASIC AUTH, API KEY, OAUTH) # connection_arn = destination.get("ConnectionArn") headers = { # default headers AWS sends with every api destination call "User-Agent": "Amazon/EventBridge/ApiDestinations", "Content-Type": "application/json; charset=utf-8", "Range": "bytes=0-1048575", "Accept-Encoding": "gzip,deflate", "Connection": "close", } # TODO: consider option to disable the actual network call to avoid unintended side effects # TODO: InvocationRateLimitPerSecond (needs some form of thread-safety, scoped to the api destination) result = requests.request( method=method, url=endpoint, data=json.dumps(event or {}), headers=headers ) if result.status_code >= 400: LOG.debug( "Received code %s forwarding events: %s %s" % (result.status_code, method, endpoint) ) if result.status_code == 429 or 500 <= result.status_code <= 600: pass # TODO: retry logic (only retry on 429 and 5xx response status) else: eventbus_name = target_arn.split(":")[-1].split("/")[-1] events_client.put_events( Entries=[ { "EventBusName": eventbus_name, "Source": event.get("source"), "DetailType": event.get("detail-type"), "Detail": event.get("detail"), } ] ) elif ":kinesis:" in target_arn: partition_key_path = dict_utils.get_safe( target_attributes, "$.KinesisParameters.PartitionKeyPath", default_value="$.id", ) stream_name = target_arn.split("/")[-1] partition_key = dict_utils.get_safe(event, partition_key_path, event["id"]) kinesis_client = connect_to_service("kinesis", region_name=region) kinesis_client.put_record( StreamName=stream_name, Data=to_bytes(json.dumps(event)), PartitionKey=partition_key, ) else: LOG.warning('Unsupported Events rule target ARN: "%s"' % target_arn)