Skip to content

Commit 09fe5a2

Browse files
committed
Adding request methods
1 parent ed1f7d8 commit 09fe5a2

File tree

3 files changed

+123
-18
lines changed

3 files changed

+123
-18
lines changed

exporter/opentelemetry-exporter-prometheus-remote-write/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@
99
((#206)[https://github.com/open-telemetry/opentelemetry-python-contrib/pull/206])
1010
- Add conversion to TimeSeries methods
1111
((#207)[https://github.com/open-telemetry/opentelemetry-python-contrib/pull/207])
12+
- Add request methods
13+
((#212)[https://github.com/open-telemetry/opentelemetry-python-contrib/pull/212])

exporter/opentelemetry-exporter-prometheus-remote-write/src/opentelemetry/exporter/prometheus_remote_write/__init__.py

+69-6
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import logging
1516
import re
1617
from typing import Dict, Sequence
1718

19+
import requests
20+
21+
import snappy
1822
from opentelemetry.exporter.prometheus_remote_write.gen.remote_pb2 import (
1923
WriteRequest,
2024
)
@@ -36,6 +40,8 @@
3640
ValueObserverAggregator,
3741
)
3842

43+
logger = logging.getLogger(__name__)
44+
3945

4046
class PrometheusRemoteWriteMetricsExporter(MetricsExporter):
4147
"""
@@ -108,7 +114,7 @@ def timeout(self, timeout: int):
108114

109115
@property
110116
def tls_config(self):
111-
return self.tls_config
117+
return self._tls_config
112118

113119
@tls_config.setter
114120
def tls_config(self, tls_config: Dict):
@@ -148,10 +154,13 @@ def headers(self, headers: Dict):
148154
def export(
149155
self, export_records: Sequence[ExportRecord]
150156
) -> MetricsExportResult:
151-
raise NotImplementedError()
157+
timeseries = self.convert_to_timeseries(export_records)
158+
message = self.build_message(timeseries)
159+
headers = self.get_headers()
160+
return self.send_message(message, headers)
152161

153162
def shutdown(self) -> None:
154-
raise NotImplementedError()
163+
pass
155164

156165
def convert_to_timeseries(
157166
self, export_records: Sequence[ExportRecord]
@@ -280,12 +289,66 @@ def create_label(self, name: str, value: str) -> Label:
280289
return label
281290

282291
def build_message(self, timeseries: Sequence[TimeSeries]) -> bytes:
283-
raise NotImplementedError()
292+
write_request = WriteRequest()
293+
write_request.timeseries.extend(timeseries)
294+
serialized_message = write_request.SerializeToString()
295+
return snappy.compress(serialized_message)
284296

285297
def get_headers(self) -> Dict:
286-
raise NotImplementedError()
298+
headers = {
299+
"Content-Encoding": "snappy",
300+
"Content-Type": "application/x-protobuf",
301+
"X-Prometheus-Remote-Write-Version": "0.1.0",
302+
}
303+
if self.headers:
304+
for header_name, header_value in self.headers.items():
305+
headers[header_name] = header_value
306+
return headers
287307

288308
def send_message(
289309
self, message: bytes, headers: Dict
290310
) -> MetricsExportResult:
291-
raise NotImplementedError()
311+
auth = None
312+
if self.basic_auth:
313+
basic_auth = self.basic_auth
314+
if "password" in basic_auth:
315+
auth = (basic_auth.username, basic_auth.password)
316+
else:
317+
with open(basic_auth.password_file) as file:
318+
auth = (basic_auth.username, file.readline())
319+
320+
cert = None
321+
verify = True
322+
if self.tls_config:
323+
if "ca_file" in self.tls_config:
324+
verify = self.tls_config["ca_file"]
325+
elif "insecure_skip_verify" in self.tls_config:
326+
verify = self.tls_config["insecure_skip_verify"]
327+
328+
if (
329+
"cert_file" in self.tls_config
330+
and "key_file" in self.tls_config
331+
):
332+
cert = (
333+
self.tls_config["cert_file"],
334+
self.tls_config["key_file"],
335+
)
336+
response = requests.post(
337+
self.endpoint,
338+
data=message,
339+
headers=headers,
340+
auth=auth,
341+
timeout=self.timeout,
342+
proxies=self.proxies,
343+
cert=cert,
344+
verify=verify,
345+
)
346+
if response.status_code != 200:
347+
logger.warning(
348+
"POST request failed with status %s with reason: %s and content: %s",
349+
str(response.status_code),
350+
response.reason,
351+
str(response.content),
352+
)
353+
return MetricsExportResult.FAILURE
354+
return MetricsExportResult.SUCCESS

exporter/opentelemetry-exporter-prometheus-remote-write/tests/test_prometheus_remote_write_exporter.py

+52-12
Original file line numberDiff line numberDiff line change
@@ -309,25 +309,65 @@ def test_create_timeseries(self):
309309
self.assertEqual(timeseries, expected_timeseries)
310310

311311

312+
class ResponseStub:
313+
def __init__(self, status_code):
314+
self.status_code = status_code
315+
self.reason = "dummy_reason"
316+
self.content = "dummy_content"
317+
318+
312319
class TestExport(unittest.TestCase):
313320
# Initializes test data that is reused across tests
314321
def setUp(self):
315-
pass
322+
self._exporter = PrometheusRemoteWriteMetricsExporter(
323+
endpoint="/prom/test_endpoint"
324+
)
316325

317326
# Ensures export is successful with valid export_records and config
318-
def test_export(self):
319-
pass
320-
321-
def test_valid_send_message(self):
322-
pass
323-
324-
def test_invalid_send_message(self):
325-
pass
327+
@mock.patch("requests.post", return_value=ResponseStub(200))
328+
def test_export(self, mock_post):
329+
test_metric = Counter("testname", "testdesc", "testunit", int, None)
330+
labels = {"environment": "testing"}
331+
record = ExportRecord(
332+
test_metric, labels, SumAggregator(), Resource({}),
333+
)
334+
result = self._exporter.export([record])
335+
self.assertIs(result, MetricsExportResult.SUCCESS)
336+
self.assertEqual(mock_post.call_count, 1)
337+
338+
@mock.patch("requests.post", return_value=ResponseStub(200))
339+
def test_valid_send_message(self, mock_post):
340+
result = self._exporter.send_message(bytes(), {})
341+
self.assertEqual(mock_post.call_count, 1)
342+
self.assertEqual(result, MetricsExportResult.SUCCESS)
343+
344+
@mock.patch("requests.post", return_value=ResponseStub(404))
345+
def test_invalid_send_message(self, mock_post):
346+
result = self._exporter.send_message(bytes(), {})
347+
self.assertEqual(mock_post.call_count, 1)
348+
self.assertEqual(result, MetricsExportResult.FAILURE)
326349

327350
# Verifies that build_message calls snappy.compress and returns SerializedString
328-
def test_build_message(self):
329-
pass
351+
@mock.patch("snappy.compress", return_value=bytes())
352+
def test_build_message(self, mock_compress):
353+
test_timeseries = [
354+
TimeSeries(),
355+
TimeSeries(),
356+
]
357+
message = self._exporter.build_message(test_timeseries)
358+
self.assertEqual(mock_compress.call_count, 1)
359+
self.assertIsInstance(message, bytes)
330360

331361
# Ensure correct headers are added when valid config is provided
332362
def test_get_headers(self):
333-
pass
363+
self._exporter.headers = {"Custom Header": "test_header"}
364+
365+
headers = self._exporter.get_headers()
366+
self.assertEqual(headers.get("Content-Encoding", ""), "snappy")
367+
self.assertEqual(
368+
headers.get("Content-Type", ""), "application/x-protobuf"
369+
)
370+
self.assertEqual(
371+
headers.get("X-Prometheus-Remote-Write-Version", ""), "0.1.0"
372+
)
373+
self.assertEqual(headers.get("Custom Header", ""), "test_header")

0 commit comments

Comments
 (0)