diff --git a/aptly_api/parts/misc.py b/aptly_api/parts/misc.py index a1f21b1..e59dad3 100644 --- a/aptly_api/parts/misc.py +++ b/aptly_api/parts/misc.py @@ -18,3 +18,17 @@ def version(self) -> str: return cast(str, resp.json()["Version"]) else: raise AptlyAPIException("Aptly server didn't return a valid response object:\n%s" % resp.text) + + def metrics(self) -> str: + try: + resp = self.do_get("api/metrics") + except AptlyAPIException as error: + if error.status_code == 404: + raise NotImplementedError from error + raise + + if resp.status_code != 200: + raise AptlyAPIException(self._error_from_response(resp), status_code=resp.status_code) + + return resp.text + diff --git a/aptly_api/tests/test_data/metrics.txt b/aptly_api/tests/test_data/metrics.txt new file mode 100644 index 0000000..ea4646c --- /dev/null +++ b/aptly_api/tests/test_data/metrics.txt @@ -0,0 +1,183 @@ +# HELP aptly_api_http_request_duration_seconds Duration of api requests in seconds. +# TYPE aptly_api_http_request_duration_seconds summary +aptly_api_http_request_duration_seconds_sum{code="200",method="GET",path="/api/files"} 0.006822909 +aptly_api_http_request_duration_seconds_count{code="200",method="GET",path="/api/files"} 1 +aptly_api_http_request_duration_seconds_sum{code="200",method="GET",path="/api/mirrors"} 0.0070748 +aptly_api_http_request_duration_seconds_count{code="200",method="GET",path="/api/mirrors"} 1 +aptly_api_http_request_duration_seconds_sum{code="200",method="GET",path="/api/publish"} 0.006484062 +aptly_api_http_request_duration_seconds_count{code="200",method="GET",path="/api/publish"} 1 +aptly_api_http_request_duration_seconds_sum{code="200",method="GET",path="/api/repos"} 0.006959955 +aptly_api_http_request_duration_seconds_count{code="200",method="GET",path="/api/repos"} 1 +aptly_api_http_request_duration_seconds_sum{code="200",method="GET",path="/api/snapshots"} 0.006250874 +aptly_api_http_request_duration_seconds_count{code="200",method="GET",path="/api/snapshots"} 1 +aptly_api_http_request_duration_seconds_sum{code="200",method="GET",path="/api/version"} 0.254958217 +aptly_api_http_request_duration_seconds_count{code="200",method="GET",path="/api/version"} 2 +aptly_api_http_request_duration_seconds_sum{code="404",method="GET",path="/api/healthy"} 0.011135046 +aptly_api_http_request_duration_seconds_count{code="404",method="GET",path="/api/healthy"} 1 +# HELP aptly_api_http_request_size_bytes Api HTTP request size in bytes. +# TYPE aptly_api_http_request_size_bytes summary +aptly_api_http_request_size_bytes_sum{code="200",method="GET",path="/api/files"} 0 +aptly_api_http_request_size_bytes_count{code="200",method="GET",path="/api/files"} 1 +aptly_api_http_request_size_bytes_sum{code="200",method="GET",path="/api/mirrors"} 0 +aptly_api_http_request_size_bytes_count{code="200",method="GET",path="/api/mirrors"} 1 +aptly_api_http_request_size_bytes_sum{code="200",method="GET",path="/api/publish"} 0 +aptly_api_http_request_size_bytes_count{code="200",method="GET",path="/api/publish"} 1 +aptly_api_http_request_size_bytes_sum{code="200",method="GET",path="/api/repos"} 0 +aptly_api_http_request_size_bytes_count{code="200",method="GET",path="/api/repos"} 1 +aptly_api_http_request_size_bytes_sum{code="200",method="GET",path="/api/snapshots"} 0 +aptly_api_http_request_size_bytes_count{code="200",method="GET",path="/api/snapshots"} 1 +aptly_api_http_request_size_bytes_sum{code="200",method="GET",path="/api/version"} 0 +aptly_api_http_request_size_bytes_count{code="200",method="GET",path="/api/version"} 2 +aptly_api_http_request_size_bytes_sum{code="404",method="GET",path="/api/healthy"} 0 +aptly_api_http_request_size_bytes_count{code="404",method="GET",path="/api/healthy"} 1 +# HELP aptly_api_http_requests_in_flight Number of concurrent HTTP api requests currently handled. +# TYPE aptly_api_http_requests_in_flight gauge +aptly_api_http_requests_in_flight{method="GET",path="/api/files"} 0 +aptly_api_http_requests_in_flight{method="GET",path="/api/healthy"} 0 +aptly_api_http_requests_in_flight{method="GET",path="/api/metrics"} 1 +aptly_api_http_requests_in_flight{method="GET",path="/api/mirrors"} 0 +aptly_api_http_requests_in_flight{method="GET",path="/api/publish"} 0 +aptly_api_http_requests_in_flight{method="GET",path="/api/repos"} 0 +aptly_api_http_requests_in_flight{method="GET",path="/api/snapshots"} 0 +aptly_api_http_requests_in_flight{method="GET",path="/api/version"} 0 +# HELP aptly_api_http_requests_total Total number of api requests. +# TYPE aptly_api_http_requests_total counter +aptly_api_http_requests_total{code="200",method="GET",path="/api/files"} 1 +aptly_api_http_requests_total{code="200",method="GET",path="/api/mirrors"} 1 +aptly_api_http_requests_total{code="200",method="GET",path="/api/publish"} 1 +aptly_api_http_requests_total{code="200",method="GET",path="/api/repos"} 1 +aptly_api_http_requests_total{code="200",method="GET",path="/api/snapshots"} 1 +aptly_api_http_requests_total{code="200",method="GET",path="/api/version"} 2 +aptly_api_http_requests_total{code="404",method="GET",path="/api/healthy"} 1 +# HELP aptly_api_http_response_size_bytes Api HTTP response size in bytes. +# TYPE aptly_api_http_response_size_bytes summary +aptly_api_http_response_size_bytes_sum{code="200",method="GET",path="/api/files"} 2 +aptly_api_http_response_size_bytes_count{code="200",method="GET",path="/api/files"} 1 +aptly_api_http_response_size_bytes_sum{code="200",method="GET",path="/api/mirrors"} 2 +aptly_api_http_response_size_bytes_count{code="200",method="GET",path="/api/mirrors"} 1 +aptly_api_http_response_size_bytes_sum{code="200",method="GET",path="/api/publish"} 2 +aptly_api_http_response_size_bytes_count{code="200",method="GET",path="/api/publish"} 1 +aptly_api_http_response_size_bytes_sum{code="200",method="GET",path="/api/repos"} 2 +aptly_api_http_response_size_bytes_count{code="200",method="GET",path="/api/repos"} 1 +aptly_api_http_response_size_bytes_sum{code="200",method="GET",path="/api/snapshots"} 2 +aptly_api_http_response_size_bytes_count{code="200",method="GET",path="/api/snapshots"} 1 +aptly_api_http_response_size_bytes_sum{code="200",method="GET",path="/api/version"} 80 +aptly_api_http_response_size_bytes_count{code="200",method="GET",path="/api/version"} 2 +aptly_api_http_response_size_bytes_sum{code="404",method="GET",path="/api/healthy"} 0 +aptly_api_http_response_size_bytes_count{code="404",method="GET",path="/api/healthy"} 1 +# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 2.5597e-05 +go_gc_duration_seconds{quantile="0.25"} 3.0026e-05 +go_gc_duration_seconds{quantile="0.5"} 3.7642e-05 +go_gc_duration_seconds{quantile="0.75"} 4.1318e-05 +go_gc_duration_seconds{quantile="1"} 6.2107e-05 +go_gc_duration_seconds_sum 0.000368922 +go_gc_duration_seconds_count 10 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 13 +# HELP go_info Information about the Go environment. +# TYPE go_info gauge +go_info{version="go1.22.2"} 1 +# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes 7.682272e+06 +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# TYPE go_memstats_alloc_bytes_total counter +go_memstats_alloc_bytes_total 7.818616e+07 +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# TYPE go_memstats_buck_hash_sys_bytes gauge +go_memstats_buck_hash_sys_bytes 10237 +# HELP go_memstats_frees_total Total number of frees. +# TYPE go_memstats_frees_total counter +go_memstats_frees_total 17358 +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# TYPE go_memstats_gc_sys_bytes gauge +go_memstats_gc_sys_bytes 2.974176e+06 +# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use. +# TYPE go_memstats_heap_alloc_bytes gauge +go_memstats_heap_alloc_bytes 7.682272e+06 +# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. +# TYPE go_memstats_heap_idle_bytes gauge +go_memstats_heap_idle_bytes 1.0633216e+07 +# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. +# TYPE go_memstats_heap_inuse_bytes gauge +go_memstats_heap_inuse_bytes 9.388032e+06 +# HELP go_memstats_heap_objects Number of allocated objects. +# TYPE go_memstats_heap_objects gauge +go_memstats_heap_objects 18764 +# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS. +# TYPE go_memstats_heap_released_bytes gauge +go_memstats_heap_released_bytes 1.425408e+06 +# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. +# TYPE go_memstats_heap_sys_bytes gauge +go_memstats_heap_sys_bytes 2.0021248e+07 +# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection. +# TYPE go_memstats_last_gc_time_seconds gauge +go_memstats_last_gc_time_seconds 1.7220174533593223e+09 +# HELP go_memstats_lookups_total Total number of pointer lookups. +# TYPE go_memstats_lookups_total counter +go_memstats_lookups_total 0 +# HELP go_memstats_mallocs_total Total number of mallocs. +# TYPE go_memstats_mallocs_total counter +go_memstats_mallocs_total 36122 +# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. +# TYPE go_memstats_mcache_inuse_bytes gauge +go_memstats_mcache_inuse_bytes 19200 +# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. +# TYPE go_memstats_mcache_sys_bytes gauge +go_memstats_mcache_sys_bytes 31200 +# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. +# TYPE go_memstats_mspan_inuse_bytes gauge +go_memstats_mspan_inuse_bytes 228800 +# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. +# TYPE go_memstats_mspan_sys_bytes gauge +go_memstats_mspan_sys_bytes 244800 +# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. +# TYPE go_memstats_next_gc_bytes gauge +go_memstats_next_gc_bytes 1.599968e+07 +# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. +# TYPE go_memstats_other_sys_bytes gauge +go_memstats_other_sys_bytes 3.054611e+06 +# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator. +# TYPE go_memstats_stack_inuse_bytes gauge +go_memstats_stack_inuse_bytes 950272 +# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. +# TYPE go_memstats_stack_sys_bytes gauge +go_memstats_stack_sys_bytes 950272 +# HELP go_memstats_sys_bytes Number of bytes obtained from system. +# TYPE go_memstats_sys_bytes gauge +go_memstats_sys_bytes 2.7286544e+07 +# HELP go_threads Number of OS threads created. +# TYPE go_threads gauge +go_threads 20 +# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. +# TYPE process_cpu_seconds_total counter +process_cpu_seconds_total 0.13 +# HELP process_max_fds Maximum number of open file descriptors. +# TYPE process_max_fds gauge +process_max_fds 1.073741816e+09 +# HELP process_open_fds Number of open file descriptors. +# TYPE process_open_fds gauge +process_open_fds 12 +# HELP process_resident_memory_bytes Resident memory size in bytes. +# TYPE process_resident_memory_bytes gauge +process_resident_memory_bytes 3.7937152e+07 +# HELP process_start_time_seconds Start time of the process since unix epoch in seconds. +# TYPE process_start_time_seconds gauge +process_start_time_seconds 1.72201739466e+09 +# HELP process_virtual_memory_bytes Virtual memory size in bytes. +# TYPE process_virtual_memory_bytes gauge +process_virtual_memory_bytes 2.647494656e+09 +# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes. +# TYPE process_virtual_memory_max_bytes gauge +process_virtual_memory_max_bytes 1.8446744073709552e+19 +# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. +# TYPE promhttp_metric_handler_requests_in_flight gauge +promhttp_metric_handler_requests_in_flight 1 +# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. +# TYPE promhttp_metric_handler_requests_total counter +promhttp_metric_handler_requests_total{code="200"} 0 +promhttp_metric_handler_requests_total{code="500"} 0 +promhttp_metric_handler_requests_total{code="503"} 0 diff --git a/aptly_api/tests/test_misc.py b/aptly_api/tests/test_misc.py index 18040a2..8c65b2f 100644 --- a/aptly_api/tests/test_misc.py +++ b/aptly_api/tests/test_misc.py @@ -30,3 +30,20 @@ def test_version_error(self, *, rmock: requests_mock.Mocker) -> None: rmock.get("http://test/api/version", text='{"droenk": "blah"}') with self.assertRaises(AptlyAPIException): self.mapi.version() + + def test_metrics(self, *, rmock: requests_mock.Mocker) -> None: + with open('./aptly_api/tests/test_data/metrics.txt') as test_file: + test_data = test_file.read() + rmock.get("http://test/api/metrics", text=test_data) + self.assertEqual(self.mapi.metrics(), test_data) + + def test_metrics_non_200_status_code(self, *, rmock: requests_mock.Mocker) -> None: + rmock.register_uri("GET", "http://test/api/metrics", status_code=204, text="") + with self.assertRaises(AptlyAPIException): + self.mapi.metrics() + + def test_metrics_api_disabled_or_missing(self, *, rmock: requests_mock.Mocker) -> None: + rmock.register_uri("GET", "http://test/api/metrics", status_code=404, text="404 page not found") + with self.assertRaises(NotImplementedError): + self.mapi.metrics() +