Skip to content

Commit 8a9aa62

Browse files
committed
chore: address PR comments
1 parent 291aba5 commit 8a9aa62

File tree

3 files changed

+111
-3
lines changed

3 files changed

+111
-3
lines changed

holmes/plugins/toolsets/prometheus/prometheus.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
ToolsetTag,
2323
)
2424
from holmes.plugins.toolsets.consts import STANDARD_END_DATETIME_TOOL_PARAM_DESCRIPTION
25+
from holmes.plugins.toolsets.prometheus.utils import parse_duration_to_seconds
2526
from holmes.plugins.toolsets.service_discovery import PrometheusDiscovery
2627
from holmes.plugins.toolsets.utils import (
2728
get_param_or_raise,
@@ -931,7 +932,7 @@ def __init__(self, toolset: "PrometheusToolset"):
931932
"step": ToolParameter(
932933
description="Query resolution step width in duration format or float number of seconds",
933934
type="number",
934-
required=True,
935+
required=False,
935936
),
936937
"output_type": ToolParameter(
937938
description="Specifies how to interpret the Prometheus result. Use 'Plain' for raw values, 'Bytes' to format byte values, 'Percentage' to scale 0–1 values into 0–100%, or 'CPUUsage' to convert values to cores (e.g., 500 becomes 500m, 2000 becomes 2).",
@@ -961,13 +962,13 @@ def _invoke(
961962
end_timestamp=params.get("end"),
962963
default_time_span_seconds=DEFAULT_GRAPH_TIME_SPAN_SECONDS,
963964
)
964-
step = params.get("step")
965+
step = parse_duration_to_seconds(params.get("step"))
965966

966967
# adjust_step_for_max_points handles None case and converts to float
967968
step = adjust_step_for_max_points(
968969
start_timestamp=start,
969970
end_timestamp=end,
970-
step=float(step) if step else None,
971+
step=step,
971972
)
972973

973974
description = params.get("description", "")
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import re
2+
from typing import Optional
3+
4+
from git import Union
5+
6+
7+
def parse_duration_to_seconds(v: Optional[Union[str, float, int]]) -> Optional[float]:
8+
if v is None:
9+
return None
10+
if isinstance(v, (int, float)):
11+
return float(v)
12+
s = v.strip().lower()
13+
if s.isdigit():
14+
return float(int(s))
15+
16+
units = {"s": 1, "m": 60, "h": 3600, "d": 86400}
17+
18+
# Check for partial time formats (e.g., 1h30m, 5m12s, 1d2h30m)
19+
pattern = r"(\d+(?:\.\d+)?)(d|h|m|s)"
20+
matches = re.findall(pattern, s)
21+
22+
if matches:
23+
total_seconds = 0.0
24+
for value_str, unit in matches:
25+
value = float(value_str)
26+
total_seconds += value * units[unit]
27+
return float(int(total_seconds))
28+
29+
# fallback: try float seconds
30+
return float(s)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import pytest
2+
from holmes.plugins.toolsets.prometheus.utils import parse_duration_to_seconds
3+
4+
5+
class TestParseDurationToSeconds:
6+
@pytest.mark.parametrize(
7+
"input_value,expected",
8+
[
9+
# None case
10+
(None, None),
11+
# Numeric inputs (int and float)
12+
(42, 42.0),
13+
(3.14, 3.14),
14+
(0, 0.0),
15+
# String numeric inputs
16+
("123", 123.0),
17+
("0", 0.0),
18+
(" 456 ", 456.0), # with whitespace
19+
# Time unit strings
20+
("30s", 30.0),
21+
("5m", 300.0),
22+
("2h", 7200.0),
23+
("1d", 86400.0),
24+
# Decimal values with units
25+
("2.5s", 2.0), # int(float(2.5) * 1) = 2
26+
("1.5m", 90.0), # int(float(1.5) * 60) = 90
27+
("0.5h", 1800.0), # int(float(0.5) * 3600) = 1800
28+
("0.25d", 21600.0), # int(float(0.25) * 86400) = 21600
29+
# Case insensitive and whitespace handling
30+
("30S", 30.0),
31+
("5M", 300.0),
32+
("2H", 7200.0),
33+
("1D", 86400.0),
34+
(" 30s ", 30.0),
35+
# Fallback to float seconds
36+
("123.45", 123.45),
37+
("0.5", 0.5),
38+
# Edge cases
39+
("10", 10.0), # pure digit string
40+
("0s", 0.0),
41+
("0m", 0.0),
42+
("0h", 0.0),
43+
("0d", 0.0),
44+
# Partial time formats
45+
("1h30m", 5400.0), # 1*3600 + 30*60 = 5400
46+
("2h45m", 9900.0), # 2*3600 + 45*60 = 9900
47+
("5m12s", 312.0), # 5*60 + 12 = 312
48+
("1m30s", 90.0), # 1*60 + 30 = 90
49+
("3h15m45s", 11745.0), # 3*3600 + 15*60 + 45 = 11745
50+
("1d2h30m", 95400.0), # 1*86400 + 2*3600 + 30*60 = 95400
51+
("2d1h", 176400.0), # 2*86400 + 1*3600 = 176400
52+
("30m15s", 1815.0), # 30*60 + 15 = 1815
53+
("1h0m30s", 3630.0), # 1*3600 + 0*60 + 30 = 3630
54+
("0h5m", 300.0), # 0*3600 + 5*60 = 300
55+
# Case insensitive partial times
56+
("1H30M", 5400.0),
57+
("5M12S", 312.0),
58+
(" 1h30m ", 5400.0), # with whitespace
59+
],
60+
)
61+
def test_parse_duration_to_seconds(self, input_value, expected):
62+
result = parse_duration_to_seconds(input_value)
63+
assert result == expected
64+
65+
@pytest.mark.parametrize(
66+
"invalid_input",
67+
[
68+
"invalid",
69+
"10x", # unsupported unit
70+
"abc123",
71+
"",
72+
],
73+
)
74+
def test_parse_duration_to_seconds_invalid_fallback(self, invalid_input):
75+
# These should raise ValueError when trying to convert to float
76+
with pytest.raises(ValueError):
77+
parse_duration_to_seconds(invalid_input)

0 commit comments

Comments
 (0)