Skip to content

Commit a34e899

Browse files
committed
added extra test
1 parent 6462b66 commit a34e899

File tree

1 file changed

+95
-91
lines changed

1 file changed

+95
-91
lines changed

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_otlp_sigv4_exporter.py

Lines changed: 95 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22
# SPDX-License-Identifier: Apache-2.0
33
import os
44
import time
5-
from botocore.credentials import Credentials
65
from unittest import TestCase
76
from unittest.mock import ANY, MagicMock, PropertyMock, patch
8-
from opentelemetry.sdk.trace import Tracer, Resource, SpanLimits, SpanContext
9-
from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
10-
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
11-
12-
from opentelemetry.trace import SpanKind
137

148
import requests
9+
from botocore.credentials import Credentials
1510

1611
from amazon.opentelemetry.distro.aws_opentelemetry_configurator import OTLPAwsSigV4Exporter
1712
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
@@ -24,13 +19,18 @@
2419
from opentelemetry.sdk.environment_variables import (
2520
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
2621
)
27-
28-
from opentelemetry.sdk.trace import _Span
29-
from opentelemetry.trace import SpanKind, TraceFlags
30-
from opentelemetry.sdk.trace import Resource
22+
from opentelemetry.sdk.trace import Resource, SpanContext, SpanLimits, Tracer, _Span
3123
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
24+
from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
25+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
26+
from opentelemetry.trace import SpanKind, TraceFlags
3227

3328
OTLP_CW_ENDPOINT = "https://xray.us-east-1.amazonaws.com/v1/traces"
29+
USER_AGENT = "OTel-OTLP-Exporter-Python/" + __version__
30+
CONTENT_TYPE = "application/x-protobuf"
31+
AUTHORIZATION_HEADER = "Authorization"
32+
X_AMZ_DATE_HEADER = "X-Amz-Date"
33+
X_AMZ_SECURITY_TOKEN_HEADER = "X-Amz-Security-Token"
3434

3535

3636
class TestAwsSigV4Exporter(TestCase):
@@ -40,19 +40,18 @@ def setUp(self):
4040
self.create_span("test_span2", SpanKind.SERVER),
4141
self.create_span("test_span3", SpanKind.CLIENT),
4242
self.create_span("test_span4", SpanKind.PRODUCER),
43-
self.create_span("test_span5", SpanKind.CONSUMER)
43+
self.create_span("test_span5", SpanKind.CONSUMER),
4444
]
4545

4646
self.invalid_cw_otlp_tracing_endpoints = [
4747
"https://xray.bad-region-1.amazonaws.com/v1/traces",
4848
"https://xray.us-east-1.amaz.com/v1/traces",
49-
"https://logs.us-east-1.amazonaws.com/v1/logs"
50-
"https://test-endpoint123.com/test"
49+
"https://logs.us-east-1.amazonaws.com/v1/logs" "https://test-endpoint123.com/test",
5150
]
5251

53-
self.expected_auth_header = 'AWS4-HMAC-SHA256 Credential=test_key/some_date/us-east-1/xray/aws4_request'
54-
self.expected_auth_x_amz_date = 'some_date'
55-
self.expected_auth_security_token = 'test_token'
52+
self.expected_auth_header = "AWS4-HMAC-SHA256 Credential=test_key/some_date/us-east-1/xray/aws4_request"
53+
self.expected_auth_x_amz_date = "some_date"
54+
self.expected_auth_security_token = "test_token"
5655

5756
# Tests that the default exporter is OTLP protobuf/http Span Exporter if no endpoint is set
5857
@patch.dict(os.environ, {}, clear=True)
@@ -61,7 +60,7 @@ def test_sigv4_exporter_init_default(self):
6160
self.validate_exporter_extends_http_span_exporter(exporter, DEFAULT_ENDPOINT + DEFAULT_TRACES_EXPORT_PATH)
6261
self.assertIsInstance(exporter._session, requests.Session)
6362

64-
# Tests that the endpoint is validated and sets the aws_region but still uses the OTLP protobuf/http
63+
# Tests that the endpoint is validated and sets the aws_region but still uses the OTLP protobuf/http
6564
# Span Exporter exporter constructor behavior if a valid OTLP CloudWatch endpoint is set
6665
@patch.dict(os.environ, {OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: OTLP_CW_ENDPOINT}, clear=True)
6766
@patch("botocore.session.Session")
@@ -77,7 +76,7 @@ def test_sigv4_exporter_init_valid_cw_otlp_endpoint(self, session_mock):
7776

7877
mock_session.get_available_regions.assert_called_once_with("xray")
7978

80-
# Tests that the exporter constructor behavior is set by OTLP protobuf/http Span Exporter
79+
# Tests that the exporter constructor behavior is set by OTLP protobuf/http Span Exporter
8180
# if an invalid OTLP CloudWatch endpoint is set
8281
@patch("botocore.session.Session")
8382
def test_sigv4_exporter_init_invalid_cw_otlp_endpoint(self, botocore_mock):
@@ -95,136 +94,144 @@ def test_sigv4_exporter_init_invalid_cw_otlp_endpoint(self, botocore_mock):
9594

9695
self.assertIsNone(exporter._aws_region)
9796

98-
9997
# Tests that if the OTLP endpoint is not a valid CW endpoint but the credentials are valid, SigV4 authentication method is NOT called and
10098
# is NOT injected into the existing Session headers.
10199
@patch("botocore.session.Session.get_available_regions")
102100
@patch("requests.Session.post")
103101
@patch("botocore.auth.SigV4Auth.add_auth")
104-
def test_sigv4_exporter_export_does_not_add_sigv4_if_not_valid_cw_endpoint(self, mock_sigv4_auth, requests_mock, botocore_mock):
102+
def test_sigv4_exporter_export_does_not_add_sigv4_if_not_valid_cw_endpoint(
103+
self, mock_sigv4_auth, requests_mock, botocore_mock
104+
):
105105
# Setting the exporter response
106106
mock_response = MagicMock()
107107
mock_response.status_code = 200
108108
type(mock_response).ok = PropertyMock(return_value=True)
109-
109+
110110
# Setting the request session headers to make the call to endpoint
111111
mock_session = MagicMock()
112-
mock_session.headers = {
113-
'User-Agent': 'OTel-OTLP-Exporter-Python/' + __version__,
114-
'Content-Type': 'application/x-protobuf'
115-
}
112+
mock_session.headers = {"User-Agent": USER_AGENT, "Content-Type": CONTENT_TYPE}
116113
requests_mock.return_value = mock_session
117114
mock_session.post.return_value = mock_response
118115

119116
mock_botocore_session = MagicMock()
120117
botocore_mock.return_value = mock_botocore_session
121118
mock_botocore_session.get_available_regions.return_value = ["us-east-1", "us-west-2"]
122119
mock_botocore_session.get_credentials.return_value = Credentials(
123-
access_key="test_key",
124-
secret_key="test_secret",
125-
token="test_token"
120+
access_key="test_key", secret_key="test_secret", token="test_token"
126121
)
127122

128-
#SigV4 mock authentication injection
123+
# SigV4 mock authentication injection
129124
mock_sigv4_auth.side_effect = self.mock_add_auth
130125

131126
# Initialize and call exporter
132127
exporter = OTLPAwsSigV4Exporter(endpoint=OTLP_CW_ENDPOINT)
133128
exporter.export(self.testing_spans)
134129

135-
# For each invalid CW OTLP endpoint, vdalidate that the sigv4
130+
# For each invalid CW OTLP endpoint, vdalidate that the sigv4
136131
for bad_endpoint in self.invalid_cw_otlp_tracing_endpoints:
137132
with self.subTest(endpoint=bad_endpoint):
138133
with patch.dict(os.environ, {OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: bad_endpoint}):
139134
botocore_mock.return_value = ["us-east-1", "us-west-2"]
140135

141136
exporter = OTLPAwsSigV4Exporter(endpoint=bad_endpoint)
142-
137+
143138
self.validate_exporter_extends_http_span_exporter(exporter, bad_endpoint)
139+
140+
# Test case, should not have detected a valid region
144141
self.assertIsNone(exporter._aws_region)
145-
142+
146143
exporter.export(self.testing_spans)
147144

148145
mock_sigv4_auth.assert_not_called()
149-
146+
150147
# Verify that SigV4 request headers were not injected
151148
actual_headers = mock_session.headers
152-
self.assertNotIn('Authorization', actual_headers)
153-
self.assertNotIn('X-Amz-Date', actual_headers)
154-
self.assertNotIn('X-Amz-Security-Token', actual_headers)
155-
149+
self.assertNotIn(AUTHORIZATION_HEADER, actual_headers)
150+
self.assertNotIn(X_AMZ_DATE_HEADER, actual_headers)
151+
self.assertNotIn(X_AMZ_SECURITY_TOKEN_HEADER, actual_headers)
152+
156153
requests_mock.assert_called_with(
157-
url=bad_endpoint,
158-
data=ANY,
159-
verify=ANY,
160-
timeout=ANY,
161-
cert=ANY,
162-
)
154+
url=bad_endpoint,
155+
data=ANY,
156+
verify=ANY,
157+
timeout=ANY,
158+
cert=ANY,
159+
)
163160

164161
# Tests that if the OTLP endpoint is a valid CW endpoint but no credentials are returned, SigV4 authentication method is NOT called and
165162
# is NOT injected into the existing Session headers.
166-
@patch("botocore.session.Session.get_available_regions")
163+
@patch("botocore.session.Session")
167164
@patch("requests.Session")
168165
@patch("botocore.auth.SigV4Auth.add_auth")
169166
@patch.dict(os.environ, {OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: OTLP_CW_ENDPOINT})
170-
def test_sigv4_exporter_export_does_not_add_sigv4_if_no_credentials(self, mock_sigv4, requests_mock, botcore_mock):
167+
def test_sigv4_exporter_export_does_not_add_sigv4_if_not_valid_credentials(
168+
self, mock_sigv4_auth, requests_posts_mock, botocore_mock
169+
):
170+
171+
# Setting the exporter response
171172
mock_response = MagicMock()
172173
mock_response.status_code = 200
173174
type(mock_response).ok = PropertyMock(return_value=True)
174-
175-
requests_mock.return_value = mock_response
176175

177-
botcore_mock.return_value = ["us-east-1", "us-west-2"]
176+
# Setting the request session headers to make the call to endpoint
177+
mock_session = MagicMock()
178+
mock_session.headers = {"User-Agent": USER_AGENT, "Content-Type": CONTENT_TYPE}
179+
requests_posts_mock.return_value = mock_session
180+
mock_session.post.return_value = mock_response
181+
182+
mock_botocore_session = MagicMock()
183+
botocore_mock.return_value = mock_botocore_session
184+
mock_botocore_session.get_available_regions.return_value = ["us-east-1", "us-west-2"]
185+
186+
# Test case, return None for get credentials
187+
mock_botocore_session.get_credentials.return_value = None
178188

189+
# Initialize and call exporter
179190
exporter = OTLPAwsSigV4Exporter(endpoint=OTLP_CW_ENDPOINT)
180-
181-
self.validate_exporter_extends_http_span_exporter(exporter, OTLP_CW_ENDPOINT)
182-
self.assertIsNone(exporter._aws_region)
183-
191+
192+
# Validate that the region is valid
193+
self.assertEqual(exporter._aws_region, "us-east-1")
194+
184195
exporter.export(self.testing_spans)
185196

186-
mock_sigv4.assert_not_called()
197+
# Verify SigV4 auth was not called
198+
mock_sigv4_auth.assert_not_called()
199+
200+
# Verify that SigV4 request headers were properly injected
201+
actual_headers = mock_session.headers
202+
self.assertNotIn(AUTHORIZATION_HEADER, actual_headers)
203+
self.assertNotIn(X_AMZ_DATE_HEADER, actual_headers)
204+
self.assertNotIn(X_AMZ_SECURITY_TOKEN_HEADER, actual_headers)
187205

188-
requests_mock.assert_called_with(
189-
url=OTLP_CW_ENDPOINT,
190-
data=ANY,
191-
verify=ANY,
192-
timeout=ANY,
193-
cert=ANY,
194-
)
195-
196206
# Tests that if the OTLP endpoint is valid and credentials are valid, SigV4 authentication method is called and
197207
# is injected into the existing Session headers.
198208
@patch("botocore.session.Session")
199209
@patch("requests.Session")
200210
@patch("botocore.auth.SigV4Auth.add_auth")
201211
@patch.dict(os.environ, {OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: OTLP_CW_ENDPOINT})
202-
def test_sigv4_exporter_export_adds_sigv4_authentication_if_valid_cw_endpoint(self, mock_sigv4_auth, requests_posts_mock, botocore_mock):
212+
def test_sigv4_exporter_export_adds_sigv4_authentication_if_valid_cw_endpoint(
213+
self, mock_sigv4_auth, requests_posts_mock, botocore_mock
214+
):
203215

204216
# Setting the exporter response
205217
mock_response = MagicMock()
206218
mock_response.status_code = 200
207219
type(mock_response).ok = PropertyMock(return_value=True)
208-
220+
209221
# Setting the request session headers to make the call to endpoint
210222
mock_session = MagicMock()
211-
mock_session.headers = {
212-
'User-Agent': 'OTel-OTLP-Exporter-Python/' + __version__,
213-
'Content-Type': 'application/x-protobuf'
214-
}
223+
mock_session.headers = {"User-Agent": USER_AGENT, "Content-Type": CONTENT_TYPE}
215224
requests_posts_mock.return_value = mock_session
216225
mock_session.post.return_value = mock_response
217226

218227
mock_botocore_session = MagicMock()
219228
botocore_mock.return_value = mock_botocore_session
220229
mock_botocore_session.get_available_regions.return_value = ["us-east-1", "us-west-2"]
221230
mock_botocore_session.get_credentials.return_value = Credentials(
222-
access_key="test_key",
223-
secret_key="test_secret",
224-
token="test_token"
231+
access_key="test_key", secret_key="test_secret", token="test_token"
225232
)
226233

227-
#SigV4 mock authentication injection
234+
# SigV4 mock authentication injection
228235
mock_sigv4_auth.side_effect = self.mock_add_auth
229236

230237
# Initialize and call exporter
@@ -236,14 +243,13 @@ def test_sigv4_exporter_export_adds_sigv4_authentication_if_valid_cw_endpoint(se
236243

237244
# Verify that SigV4 request headers were properly injected
238245
actual_headers = mock_session.headers
239-
self.assertIn('Authorization', actual_headers)
240-
self.assertIn('X-Amz-Date', actual_headers)
241-
self.assertIn('X-Amz-Security-Token', actual_headers)
242-
243-
self.assertEqual(actual_headers['Authorization'], self.expected_auth_header)
244-
self.assertEqual(actual_headers['X-Amz-Date'], self.expected_auth_x_amz_date)
245-
self.assertEqual(actual_headers['X-Amz-Security-Token'], self.expected_auth_security_token)
246+
self.assertIn("Authorization", actual_headers)
247+
self.assertIn("X-Amz-Date", actual_headers)
248+
self.assertIn("X-Amz-Security-Token", actual_headers)
246249

250+
self.assertEqual(actual_headers[AUTHORIZATION_HEADER], self.expected_auth_header)
251+
self.assertEqual(actual_headers[X_AMZ_DATE_HEADER], self.expected_auth_x_amz_date)
252+
self.assertEqual(actual_headers[X_AMZ_SECURITY_TOKEN_HEADER], self.expected_auth_security_token)
247253

248254
def validate_exporter_extends_http_span_exporter(self, exporter, endpoint):
249255
self.assertEqual(exporter._endpoint, endpoint)
@@ -256,13 +262,9 @@ def validate_exporter_extends_http_span_exporter(self, exporter, endpoint):
256262
self.assertIn("User-Agent", exporter._session.headers)
257263
self.assertEqual(
258264
exporter._session.headers.get("Content-Type"),
259-
"application/x-protobuf",
260-
)
261-
self.assertEqual(
262-
exporter._session.headers.get("User-Agent"),
263-
"OTel-OTLP-Exporter-Python/" + __version__,
265+
CONTENT_TYPE,
264266
)
265-
267+
self.assertEqual(exporter._session.headers.get("User-Agent"), USER_AGENT)
266268

267269
def create_span(self, name="test_span", kind=SpanKind.INTERNAL):
268270
span = _Span(
@@ -271,15 +273,17 @@ def create_span(self, name="test_span", kind=SpanKind.INTERNAL):
271273
trace_id=0x1234567890ABCDEF,
272274
span_id=0x9876543210,
273275
is_remote=False,
274-
trace_flags=TraceFlags(TraceFlags.SAMPLED)
276+
trace_flags=TraceFlags(TraceFlags.SAMPLED),
275277
),
276-
kind=kind
278+
kind=kind,
277279
)
278280
return span
279281

280282
def mock_add_auth(self, request):
281-
request.headers._headers.extend([
282-
('Authorization', self.expected_auth_header),
283-
('X-Amz-Date', self.expected_auth_x_amz_date),
284-
('X-Amz-Security-Token', self.expected_auth_security_token)
285-
])
283+
request.headers._headers.extend(
284+
[
285+
(AUTHORIZATION_HEADER, self.expected_auth_header),
286+
(X_AMZ_DATE_HEADER, self.expected_auth_x_amz_date),
287+
(X_AMZ_SECURITY_TOKEN_HEADER, self.expected_auth_security_token),
288+
]
289+
)

0 commit comments

Comments
 (0)