python-botocore/tests/unit/test_monitoring.py

993 lines
35 KiB
Python
Raw Normal View History

2018-12-28 08:05:06 +01:00
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import json
import re
import socket
import time
from botocore.awsrequest import AWSRequest
from botocore.exceptions import ConnectionError
from botocore.hooks import HierarchicalEmitter
2022-05-26 00:10:07 +02:00
from botocore.model import OperationModel, ServiceModel
from botocore.monitoring import (
APICallAttemptEvent,
APICallEvent,
BaseMonitorEvent,
CSMSerializer,
Monitor,
MonitorEventAdapter,
SocketPublisher,
)
from tests import mock, unittest
2018-12-28 08:05:06 +01:00
class PublishingException(Exception):
pass
class TestMonitor(unittest.TestCase):
def setUp(self):
self.adapter = mock.Mock(MonitorEventAdapter)
self.publisher = mock.Mock(SocketPublisher)
self.handler = Monitor(self.adapter, self.publisher)
def test_register(self):
event_emitter = mock.Mock(HierarchicalEmitter)
self.handler.register(event_emitter)
self.assertEqual(
event_emitter.register_last.call_args_list,
[
mock.call('before-parameter-build', self.handler.capture),
mock.call('request-created', self.handler.capture),
mock.call('response-received', self.handler.capture),
mock.call('after-call', self.handler.capture),
mock.call('after-call-error', self.handler.capture),
2022-05-26 00:10:07 +02:00
],
2018-12-28 08:05:06 +01:00
)
def test_handle(self):
event = object()
self.adapter.feed.return_value = event
self.handler.capture('event-name', event_parameter='event-value')
self.adapter.feed.assert_called_with(
2022-05-26 00:10:07 +02:00
'event-name', {'event_parameter': 'event-value'}
)
2018-12-28 08:05:06 +01:00
self.publisher.publish.assert_called_with(event)
def test_handle_no_publish(self):
self.adapter.feed.return_value = None
self.handler.capture('event-name', event_parameter='event-value')
self.publisher.publish.assert_not_called()
def test_handle_catches_exceptions(self):
self.publisher.publish.side_effect = PublishingException()
try:
self.handler.capture('event-name', event_parameter='event-value')
except PublishingException:
2022-05-26 00:10:07 +02:00
self.fail(
'The publishing exception should have been caught '
'in the handler'
)
2018-12-28 08:05:06 +01:00
class TestMonitorEventAdapter(unittest.TestCase):
def setUp(self):
self.mock_time = mock.Mock(time.time)
self.mock_time.return_value = 0
self.adapter = MonitorEventAdapter(self.mock_time)
self.context = {}
self.wire_name = 'MyOperation'
self.operation_model = mock.Mock(OperationModel)
self.operation_model.wire_name = self.wire_name
self.service_id = 'MyService'
self.service_model = mock.Mock(ServiceModel)
self.service_model.service_id = self.service_id
self.operation_model.service_model = self.service_model
self.url = 'https://us-east-1.myservice.amazonaws.com'
self.request_headers = {}
self.request = mock.Mock(AWSRequest)
self.request.url = self.url
self.request.headers = self.request_headers
self.request.context = self.context
self.http_status_code = 200
self.response_headers = {}
def feed_before_parameter_build_event(self, current_time=0):
self.mock_time.return_value = current_time
2022-05-26 00:10:07 +02:00
self.adapter.feed(
'before-parameter-build',
{'model': self.operation_model, 'context': self.context},
)
2018-12-28 08:05:06 +01:00
def feed_request_created_event(self, current_time=0):
self.mock_time.return_value = current_time
2022-05-26 00:10:07 +02:00
self.adapter.feed(
'request-created',
{
'request': self.request,
},
)
2018-12-28 08:05:06 +01:00
def test_feed_before_parameter_build_returns_no_event(self):
self.assertIsNone(
2022-05-26 00:10:07 +02:00
self.adapter.feed(
'before-parameter-build',
{'model': self.operation_model, 'context': self.context},
)
2018-12-28 08:05:06 +01:00
)
def test_feed_request_created_returns_no_event(self):
2022-05-26 00:10:07 +02:00
self.adapter.feed(
'before-parameter-build',
{'model': self.operation_model, 'context': self.context},
)
2018-12-28 08:05:06 +01:00
self.assertIsNone(
2022-05-26 00:10:07 +02:00
self.adapter.feed(
'request-created',
{
'request': self.request,
},
)
2018-12-28 08:05:06 +01:00
)
def test_feed_with_successful_response(self):
self.feed_before_parameter_build_event(current_time=1)
self.feed_request_created_event(current_time=2)
self.mock_time.return_value = 3
2022-05-26 00:10:07 +02:00
attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': {
'ResponseMetadata': {
'HTTPStatusCode': self.http_status_code,
'HTTPHeaders': self.response_headers,
}
},
'context': self.context,
'exception': None,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
attempt_event,
APICallAttemptEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=2000,
latency=1000,
url=self.url,
request_headers=self.request_headers,
http_status_code=self.http_status_code,
response_headers=self.response_headers,
2022-05-26 00:10:07 +02:00
),
2018-12-28 08:05:06 +01:00
)
self.mock_time.return_value = 4
2022-05-26 00:10:07 +02:00
call_event = self.adapter.feed(
'after-call',
{
'parsed': {
'ResponseMetadata': {
'HTTPStatusCode': self.http_status_code,
'HTTPHeaders': self.response_headers,
}
},
'context': self.context,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
call_event,
APICallEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=1000,
latency=3000,
2022-05-26 00:10:07 +02:00
attempts=[attempt_event],
),
2018-12-28 08:05:06 +01:00
)
def test_feed_with_retries(self):
self.feed_before_parameter_build_event(current_time=1)
self.feed_request_created_event(current_time=2)
self.mock_time.return_value = 3
2022-05-26 00:10:07 +02:00
first_attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': {
'ResponseMetadata': {
'HTTPStatusCode': 500,
'HTTPHeaders': self.response_headers,
}
},
'context': self.context,
'exception': None,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
first_attempt_event,
APICallAttemptEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=2000,
latency=1000,
url=self.url,
request_headers=self.request_headers,
http_status_code=500,
response_headers=self.response_headers,
2022-05-26 00:10:07 +02:00
),
2018-12-28 08:05:06 +01:00
)
self.feed_request_created_event(current_time=5)
self.mock_time.return_value = 6
2022-05-26 00:10:07 +02:00
second_attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': {
'ResponseMetadata': {
'HTTPStatusCode': 200,
'HTTPHeaders': self.response_headers,
}
},
'context': self.context,
'exception': None,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
second_attempt_event,
APICallAttemptEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=5000,
latency=1000,
url=self.url,
request_headers=self.request_headers,
http_status_code=200,
response_headers=self.response_headers,
2022-05-26 00:10:07 +02:00
),
2018-12-28 08:05:06 +01:00
)
self.mock_time.return_value = 7
2022-05-26 00:10:07 +02:00
call_event = self.adapter.feed(
'after-call',
{
'parsed': {
'ResponseMetadata': {
'HTTPStatusCode': 200,
'HTTPHeaders': self.response_headers,
}
},
'context': self.context,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
call_event,
APICallEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=1000,
latency=6000,
2022-05-26 00:10:07 +02:00
attempts=[first_attempt_event, second_attempt_event],
),
2018-12-28 08:05:06 +01:00
)
def test_feed_with_retries_exceeded(self):
self.feed_before_parameter_build_event(current_time=1)
self.feed_request_created_event(current_time=2)
self.mock_time.return_value = 3
2022-05-26 00:10:07 +02:00
first_attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': {
'ResponseMetadata': {
'HTTPStatusCode': 500,
'HTTPHeaders': self.response_headers,
}
},
'context': self.context,
'exception': None,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.feed_request_created_event(current_time=5)
self.mock_time.return_value = 6
2022-05-26 00:10:07 +02:00
second_attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': {
'ResponseMetadata': {
'HTTPStatusCode': 200,
'HTTPHeaders': self.response_headers,
'MaxAttemptsReached': True,
}
},
'context': self.context,
'exception': None,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.mock_time.return_value = 7
2022-05-26 00:10:07 +02:00
call_event = self.adapter.feed(
'after-call',
{
'parsed': {
'ResponseMetadata': {
'HTTPStatusCode': 200,
'HTTPHeaders': self.response_headers,
'MaxAttemptsReached': True,
}
},
'context': self.context,
2018-12-28 08:05:06 +01:00
},
2022-05-26 00:10:07 +02:00
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
call_event,
APICallEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=1000,
latency=6000,
attempts=[first_attempt_event, second_attempt_event],
2022-05-26 00:10:07 +02:00
retries_exceeded=True,
),
2018-12-28 08:05:06 +01:00
)
def test_feed_with_parsed_error(self):
self.feed_before_parameter_build_event(current_time=1)
self.feed_request_created_event(current_time=2)
self.mock_time.return_value = 3
parsed_error = {'Code': 'MyErrorCode', 'Message': 'MyMessage'}
parsed_response = {
'Error': parsed_error,
'ResponseMetadata': {
'HTTPStatusCode': 400,
2022-05-26 00:10:07 +02:00
'HTTPHeaders': self.response_headers,
},
2018-12-28 08:05:06 +01:00
}
2022-05-26 00:10:07 +02:00
attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': parsed_response,
'context': self.context,
'exception': None,
},
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
attempt_event,
APICallAttemptEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=2000,
latency=1000,
url=self.url,
request_headers=self.request_headers,
http_status_code=400,
response_headers=self.response_headers,
2022-05-26 00:10:07 +02:00
parsed_error=parsed_error,
),
2018-12-28 08:05:06 +01:00
)
self.mock_time.return_value = 4
2022-05-26 00:10:07 +02:00
call_event = self.adapter.feed(
'after-call', {'parsed': parsed_response, 'context': self.context}
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
call_event,
APICallEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=1000,
latency=3000,
2022-05-26 00:10:07 +02:00
attempts=[attempt_event],
),
2018-12-28 08:05:06 +01:00
)
def test_feed_with_wire_exception(self):
self.feed_before_parameter_build_event(current_time=1)
self.feed_request_created_event(current_time=2)
self.mock_time.return_value = 3
wire_exception = Exception('Some wire exception')
2022-05-26 00:10:07 +02:00
attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': None,
'context': self.context,
'exception': wire_exception,
},
)
2018-12-28 08:05:06 +01:00
self.assertEqual(
attempt_event,
APICallAttemptEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=2000,
latency=1000,
url=self.url,
request_headers=self.request_headers,
wire_exception=wire_exception,
2022-05-26 00:10:07 +02:00
),
2018-12-28 08:05:06 +01:00
)
self.mock_time.return_value = 4
call_event = self.adapter.feed(
2022-05-26 00:10:07 +02:00
'after-call-error',
{'exception': wire_exception, 'context': self.context},
2018-12-28 08:05:06 +01:00
)
self.assertEqual(
call_event,
APICallEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=1000,
latency=3000,
2022-05-26 00:10:07 +02:00
attempts=[attempt_event],
),
2018-12-28 08:05:06 +01:00
)
def test_feed_with_wire_exception_retries_exceeded(self):
self.feed_before_parameter_build_event(current_time=1)
self.feed_request_created_event(current_time=2)
self.mock_time.return_value = 3
# Connection errors are retryable
wire_exception = ConnectionError(error='connection issue')
2022-05-26 00:10:07 +02:00
attempt_event = self.adapter.feed(
'response-received',
{
'parsed_response': None,
'context': self.context,
'exception': wire_exception,
},
)
2018-12-28 08:05:06 +01:00
self.mock_time.return_value = 4
call_event = self.adapter.feed(
2022-05-26 00:10:07 +02:00
'after-call-error',
{'exception': wire_exception, 'context': self.context},
2018-12-28 08:05:06 +01:00
)
self.assertEqual(
call_event,
APICallEvent(
service=self.service_id,
operation=self.wire_name,
timestamp=1000,
latency=3000,
attempts=[attempt_event],
2022-05-26 00:10:07 +02:00
retries_exceeded=True,
),
2018-12-28 08:05:06 +01:00
)
class TestBaseMonitorEvent(unittest.TestCase):
def test_init_self(self):
event = BaseMonitorEvent(
service='MyService', operation='MyOperation', timestamp=1000
)
self.assertEqual(event.service, 'MyService')
self.assertEqual(event.operation, 'MyOperation')
self.assertEqual(event.timestamp, 1000)
def test_eq(self):
self.assertEqual(
BaseMonitorEvent(
service='MyService', operation='MyOperation', timestamp=1000
),
BaseMonitorEvent(
service='MyService', operation='MyOperation', timestamp=1000
2022-05-26 00:10:07 +02:00
),
2018-12-28 08:05:06 +01:00
)
def test_not_eq_different_classes(self):
self.assertNotEqual(
BaseMonitorEvent(
service='MyService', operation='MyOperation', timestamp=1000
2022-05-26 00:10:07 +02:00
),
object(),
2018-12-28 08:05:06 +01:00
)
def test_not_eq_different_attrs(self):
self.assertNotEqual(
BaseMonitorEvent(
service='MyService', operation='MyOperation', timestamp=1000
),
BaseMonitorEvent(
2022-05-26 00:10:07 +02:00
service='DifferentService',
operation='DifferentOperation',
timestamp=0,
),
2018-12-28 08:05:06 +01:00
)
class TestAPICallEvent(unittest.TestCase):
def test_init(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service='MyService',
operation='MyOperation',
timestamp=1000,
latency=2000,
attempts=[],
2018-12-28 08:05:06 +01:00
)
self.assertEqual(event.service, 'MyService')
self.assertEqual(event.operation, 'MyOperation')
self.assertEqual(event.timestamp, 1000)
self.assertEqual(event.latency, 2000)
self.assertEqual(event.attempts, [])
def test_new_api_call_attempt_event(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service='MyService',
operation='MyOperation',
timestamp=1000,
latency=2000,
attempts=[],
2018-12-28 08:05:06 +01:00
)
attempt_event = event.new_api_call_attempt(timestamp=2000)
self.assertEqual(
attempt_event,
APICallAttemptEvent(
service='MyService', operation='MyOperation', timestamp=2000
2022-05-26 00:10:07 +02:00
),
2018-12-28 08:05:06 +01:00
)
self.assertEqual(event.attempts, [attempt_event])
class TestAPICallAttemptEvent(unittest.TestCase):
def test_init(self):
url = 'https://us-east-1.myservice.amazonaws.com'
parsed_error = {'Code': 'ErrorCode', 'Message': 'ErrorMessage'}
wire_exception = Exception('Some wire exception')
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service='MyService',
operation='MyOperation',
timestamp=1000,
latency=2000,
url=url,
http_status_code=200,
request_headers={},
response_headers={},
parsed_error=parsed_error,
wire_exception=wire_exception,
2018-12-28 08:05:06 +01:00
)
self.assertEqual(event.service, 'MyService')
self.assertEqual(event.operation, 'MyOperation')
self.assertEqual(event.timestamp, 1000)
self.assertEqual(event.latency, 2000)
self.assertEqual(event.url, url)
self.assertEqual(event.http_status_code, 200)
self.assertEqual(event.request_headers, {})
self.assertEqual(event.response_headers, {})
self.assertEqual(event.parsed_error, parsed_error)
self.assertEqual(event.wire_exception, wire_exception)
class TestCSMSerializer(unittest.TestCase):
def setUp(self):
self.csm_client_id = 'MyId'
self.serializer = CSMSerializer(self.csm_client_id)
self.service = 'MyService'
self.operation = 'MyOperation'
self.user_agent = 'my-user-agent'
self.fqdn = 'us-east-1.myservice.amazonaws.com'
self.url = 'https://' + self.fqdn
self.timestamp = 1000
self.latency = 2000
2022-05-26 00:10:07 +02:00
self.request_headers = {'User-Agent': self.user_agent}
2018-12-28 08:05:06 +01:00
def get_serialized_event_dict(self, event):
serialized_event = self.serializer.serialize(event)
return json.loads(serialized_event.decode('utf-8'))
def test_validates_csm_client_id(self):
max_client_id_len = 255
with self.assertRaises(ValueError):
CSMSerializer('a' * (max_client_id_len + 1))
def test_serialize_produces_bytes(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2018-12-28 08:05:06 +01:00
serialized_event = self.serializer.serialize(event)
2022-05-26 00:10:07 +02:00
self.assertIsInstance(serialized_event, bytes)
2018-12-28 08:05:06 +01:00
def test_serialize_does_not_add_whitespace(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2018-12-28 08:05:06 +01:00
serialized_event = self.serializer.serialize(event)
self.assertIsNone(re.match(r'\s', serialized_event.decode('utf-8')))
def test_serialize_api_call_event(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict,
{
2018-12-28 08:05:06 +01:00
'Version': 1,
'Type': 'ApiCall',
'Service': self.service,
'Api': self.operation,
'ClientId': self.csm_client_id,
'MaxRetriesExceeded': 0,
'Timestamp': 1000,
'AttemptCount': 0,
2022-05-26 00:10:07 +02:00
},
2018-12-28 08:05:06 +01:00
)
def test_serialize_api_call_event_with_latency(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=1000,
latency=2000,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['Latency'], self.latency)
def test_serialize_api_call_event_with_attempts(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2018-12-28 08:05:06 +01:00
event.new_api_call_attempt(2000)
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['AttemptCount'], 1)
def test_serialize_api_call_event_region(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2018-12-28 08:05:06 +01:00
attempt = event.new_api_call_attempt(2000)
auth_value = (
'AWS4-HMAC-SHA256 '
'Credential=myaccesskey/20180523/my-region-1/ec2/aws4_request,'
'SignedHeaders=content-type;host;x-amz-date, '
'Signature=somesignature'
)
self.request_headers['Authorization'] = auth_value
attempt.request_headers = self.request_headers
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['Region'], 'my-region-1')
2019-02-27 08:30:11 +01:00
def test_serialize_api_call_event_user_agent(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2019-02-27 08:30:11 +01:00
attempt = event.new_api_call_attempt(2000)
attempt.request_headers = {'User-Agent': self.user_agent}
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['UserAgent'], self.user_agent)
def test_serialize_api_call_event_http_status_code(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2019-02-27 08:30:11 +01:00
attempt = event.new_api_call_attempt(2000)
attempt.http_status_code = 200
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['FinalHttpStatusCode'], 200)
def test_serialize_api_call_event_parsed_error(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2019-02-27 08:30:11 +01:00
attempt = event.new_api_call_attempt(2000)
attempt.parsed_error = {
'Code': 'MyErrorCode',
2022-05-26 00:10:07 +02:00
'Message': 'My error message',
2019-02-27 08:30:11 +01:00
}
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict['FinalAwsException'], 'MyErrorCode'
)
2019-02-27 08:30:11 +01:00
self.assertEqual(
serialized_event_dict['FinalAwsExceptionMessage'],
2022-05-26 00:10:07 +02:00
'My error message',
2019-02-27 08:30:11 +01:00
)
def test_serialize_api_call_event_wire_exception(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service, operation=self.operation, timestamp=1000
)
2019-02-27 08:30:11 +01:00
attempt = event.new_api_call_attempt(2000)
attempt.wire_exception = Exception('Error on the wire')
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict['FinalSdkException'], 'Exception'
)
2019-02-27 08:30:11 +01:00
self.assertEqual(
serialized_event_dict['FinalSdkExceptionMessage'],
2022-05-26 00:10:07 +02:00
'Error on the wire',
2019-02-27 08:30:11 +01:00
)
2018-12-28 08:05:06 +01:00
def test_serialize_api_call_event_with_retries_exceeded(self):
event = APICallEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=1000,
retries_exceeded=True,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['MaxRetriesExceeded'], 1)
def test_serialize_api_call_attempt_event(self):
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict,
{
2018-12-28 08:05:06 +01:00
'Version': 1,
'Type': 'ApiCallAttempt',
'Service': self.service,
'Api': self.operation,
'ClientId': self.csm_client_id,
'Timestamp': self.timestamp,
2022-05-26 00:10:07 +02:00
},
2018-12-28 08:05:06 +01:00
)
def test_serialize_api_call_attempt_event_with_latency(self):
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
latency=self.latency,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['AttemptLatency'], self.latency)
def test_serialize_with_user_agent(self):
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
2018-12-28 08:05:06 +01:00
timestamp=self.timestamp,
2022-05-26 00:10:07 +02:00
request_headers={'User-Agent': self.user_agent},
2018-12-28 08:05:06 +01:00
)
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['UserAgent'], self.user_agent)
def test_serialize_with_url(self):
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
url=self.url,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['Fqdn'], self.fqdn)
def test_serialize_with_s3_signing(self):
auth_value = 'AWS myaccesskey:somesignature'
self.request_headers['Authorization'] = auth_value
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
request_headers=self.request_headers,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['AccessKey'], 'myaccesskey')
def test_serialize_with_sigv4_sigining(self):
auth_value = (
'AWS4-HMAC-SHA256 '
'Credential=myaccesskey/20180523/my-region-1/ec2/aws4_request,'
'SignedHeaders=content-type;host;x-amz-date, '
'Signature=somesignature'
)
self.request_headers['Authorization'] = auth_value
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
request_headers=self.request_headers,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['AccessKey'], 'myaccesskey')
def test_serialize_with_session_token(self):
self.request_headers['X-Amz-Security-Token'] = 'my-security-token'
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
request_headers=self.request_headers,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict['SessionToken'], 'my-security-token'
)
2018-12-28 08:05:06 +01:00
def test_serialize_with_path_parameters_in_url(self):
self.url = 'https://' + self.fqdn + '/resource'
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
url=self.url,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['Fqdn'], self.fqdn)
def test_serialize_with_request_id_headers(self):
response_headers = {
'x-amzn-requestid': 'id1',
'x-amz-request-id': 'id2',
'x-amz-id-2': 'id3',
}
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
response_headers=response_headers,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['XAmznRequestId'], 'id1')
self.assertEqual(serialized_event_dict['XAmzRequestId'], 'id2')
self.assertEqual(serialized_event_dict['XAmzId2'], 'id3')
def test_serialize_filters_unwanted_response_headers(self):
response_headers = {'filter-out': 'do-not-include-this'}
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
response_headers=response_headers,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict,
{
2018-12-28 08:05:06 +01:00
'Version': 1,
'Type': 'ApiCallAttempt',
'Service': self.service,
'Api': self.operation,
'ClientId': self.csm_client_id,
'Timestamp': self.timestamp,
2022-05-26 00:10:07 +02:00
},
2018-12-28 08:05:06 +01:00
)
def test_serialize_with_status_code(self):
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
http_status_code=200,
)
2018-12-28 08:05:06 +01:00
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['HttpStatusCode'], 200)
def test_serialize_with_service_error(self):
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
parsed_error={
2018-12-28 08:05:06 +01:00
'Code': 'MyErrorCode',
2022-05-26 00:10:07 +02:00
'Message': 'My error message',
},
2018-12-28 08:05:06 +01:00
)
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['AwsException'], 'MyErrorCode')
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict['AwsExceptionMessage'], 'My error message'
)
2018-12-28 08:05:06 +01:00
def test_serialize_with_wire_exception(self):
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
2018-12-28 08:05:06 +01:00
timestamp=self.timestamp,
2022-05-26 00:10:07 +02:00
wire_exception=Exception('Error on the wire'),
2018-12-28 08:05:06 +01:00
)
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(serialized_event_dict['SdkException'], 'Exception')
self.assertEqual(
2022-05-26 00:10:07 +02:00
serialized_event_dict['SdkExceptionMessage'], 'Error on the wire'
)
2018-12-28 08:05:06 +01:00
def test_serialize_truncates_long_user_agent(self):
max_user_agent_length = 256
user_agent = 'a' * (max_user_agent_length + 1)
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
2018-12-28 08:05:06 +01:00
timestamp=self.timestamp,
2022-05-26 00:10:07 +02:00
request_headers={'User-Agent': user_agent},
2018-12-28 08:05:06 +01:00
)
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
serialized_event_dict['UserAgent'],
2022-05-26 00:10:07 +02:00
user_agent[:max_user_agent_length],
2018-12-28 08:05:06 +01:00
)
def test_serialize_truncates_long_service_error(self):
max_error_code_length = 128
max_error_message_length = 512
long_error_code = 'c' * (max_error_code_length + 1)
long_error_message = 'm' * (max_error_message_length + 1)
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
timestamp=self.timestamp,
parsed_error={
2018-12-28 08:05:06 +01:00
'Code': long_error_code,
2022-05-26 00:10:07 +02:00
'Message': long_error_message,
},
2018-12-28 08:05:06 +01:00
)
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
serialized_event_dict['AwsException'],
2022-05-26 00:10:07 +02:00
long_error_code[:max_error_code_length],
2018-12-28 08:05:06 +01:00
)
self.assertEqual(
serialized_event_dict['AwsExceptionMessage'],
2022-05-26 00:10:07 +02:00
long_error_message[:max_error_message_length],
2018-12-28 08:05:06 +01:00
)
def test_serialize_truncates_long_wire_exception(self):
max_class_name_length = 128
max_error_message_length = 512
long_class_name = 'W' * (max_class_name_length + 1)
wire_class = type(long_class_name, (Exception,), {})
long_error_message = 'm' * (max_error_message_length + 1)
event = APICallAttemptEvent(
2022-05-26 00:10:07 +02:00
service=self.service,
operation=self.operation,
2018-12-28 08:05:06 +01:00
timestamp=self.timestamp,
2022-05-26 00:10:07 +02:00
wire_exception=wire_class(long_error_message),
2018-12-28 08:05:06 +01:00
)
serialized_event_dict = self.get_serialized_event_dict(event)
self.assertEqual(
serialized_event_dict['SdkException'],
2022-05-26 00:10:07 +02:00
long_class_name[:max_class_name_length],
2018-12-28 08:05:06 +01:00
)
self.assertEqual(
serialized_event_dict['SdkExceptionMessage'],
2022-05-26 00:10:07 +02:00
long_error_message[:max_error_message_length],
2018-12-28 08:05:06 +01:00
)
class TestSocketPublisher(unittest.TestCase):
def setUp(self):
self.socket = mock.Mock(socket.socket)
self.host = '127.0.0.1'
self.port = 31000
self.serializer = mock.Mock(CSMSerializer)
self.publisher = SocketPublisher(
2022-05-26 00:10:07 +02:00
self.socket, self.host, self.port, self.serializer
)
2018-12-28 08:05:06 +01:00
def test_publish(self):
event = object()
self.serializer.serialize.return_value = b'serialized event'
self.publisher.publish(event)
self.serializer.serialize.assert_called_with(event)
self.socket.sendto.assert_called_with(
2022-05-26 00:10:07 +02:00
b'serialized event', (self.host, self.port)
)
2018-12-28 08:05:06 +01:00
def test_skips_publishing_over_max_size(self):
event = mock.Mock(APICallAttemptEvent)
max_event_size = 8 * 1024
self.serializer.serialize.return_value = b'a' * (max_event_size + 1)
self.publisher.publish(event)
self.socket.sendto.assert_not_called()