Skip to content

Commit 50cc010

Browse files
vitkyrkaclaude
andauthored
Add discovery port name helper (DataDog#24555)
* Add discovery port name helper * Skip empty names when matching ports by name in discovery helper An empty string in `names` would otherwise match every unnamed port (Port.name defaults to ""), which is the opposite of the intended match-by-name semantics. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Document ordering and no-fallback contract on candidate_ports_by_name The one-liner omitted the name-priority ordering and no-fallback behavior that the existing tests already assert, matching the sibling candidate_ports docstring's existing standard. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Parametrize candidate_ports_by_name tests Folds the five standalone tests into one pytest.mark.parametrize table, keeping the scenario names as ids so they stay visually aligned and distinguishable. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 7be739d commit 50cc010

4 files changed

Lines changed: 81 additions & 3 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a discovery helper for selecting service ports by name.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# (C) Datadog, Inc. 2025-present
22
# All rights reserved
33
# Licensed under a 3-clause BSD style license (see LICENSE)
4-
from .discovery import Discovery, Port, Service, candidate_ports
4+
from .discovery import Discovery, Port, Service, candidate_ports, candidate_ports_by_name
55
from .strategies import discovery_strategy
66

7-
__all__ = ['Discovery', 'Port', 'Service', 'candidate_ports', 'discovery_strategy']
7+
__all__ = ['Discovery', 'Port', 'Service', 'candidate_ports', 'candidate_ports_by_name', 'discovery_strategy']

datadog_checks_base/datadog_checks/base/utils/discovery/discovery.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,16 @@ def candidate_ports(service: Service, hints: Iterable[int]) -> Iterator[Port]:
6060
if port.number not in seen:
6161
seen.add(port.number)
6262
yield port
63+
64+
65+
def candidate_ports_by_name(service: Service, names: Iterable[str]) -> Iterator[Port]:
66+
"""Yield ports matching each name in order, with no fallback to unmatched ports."""
67+
seen: set[int] = set()
68+
69+
for name in dict.fromkeys(names):
70+
if not name:
71+
continue
72+
for port in service.ports:
73+
if port.name == name and port.number not in seen:
74+
seen.add(port.number)
75+
yield port

datadog_checks_base/tests/base/utils/discovery/test_discovery.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import mock
77
import pytest
88

9-
from datadog_checks.base.utils.discovery import Discovery, Port, Service, candidate_ports
9+
from datadog_checks.base.utils.discovery import Discovery, Port, Service, candidate_ports, candidate_ports_by_name
1010

1111

1212
def test_include_empty():
@@ -209,6 +209,70 @@ def test_candidate_ports_prefers_hints_and_deduplicates():
209209
]
210210

211211

212+
@pytest.mark.parametrize(
213+
"ports, names, expected",
214+
[
215+
pytest.param(
216+
(
217+
Port(number=8080, name='http'),
218+
Port(number=9090, name='metrics'),
219+
Port(number=8081, name='admin'),
220+
),
221+
['metrics', 'http-prom'],
222+
[Port(number=9090, name='metrics')],
223+
id="only_yields_matching_ports",
224+
),
225+
pytest.param(
226+
(
227+
Port(number=8080, name='bar'),
228+
Port(number=9090, name='foo'),
229+
Port(number=8081, name='bar'),
230+
Port(number=9091, name='foo'),
231+
),
232+
['foo', 'bar'],
233+
[
234+
Port(number=9090, name='foo'),
235+
Port(number=9091, name='foo'),
236+
Port(number=8080, name='bar'),
237+
Port(number=8081, name='bar'),
238+
],
239+
id="respects_name_priority",
240+
),
241+
pytest.param(
242+
(
243+
Port(number=8080, name='http'),
244+
Port(number=8081, name='admin'),
245+
),
246+
['metrics'],
247+
[],
248+
id="does_not_fallback_to_other_ports",
249+
),
250+
pytest.param(
251+
(
252+
Port(number=8443, name='metrics'),
253+
Port(number=8443, name='http-metrics'),
254+
Port(number=9443, name='metrics'),
255+
),
256+
['http-metrics', 'metrics'],
257+
[
258+
Port(number=8443, name='http-metrics'),
259+
Port(number=9443, name='metrics'),
260+
],
261+
id="deduplicates_matching_port_numbers",
262+
),
263+
pytest.param(
264+
(Port(number=8080, name=''),),
265+
[''],
266+
[],
267+
id="ignores_empty_names",
268+
),
269+
],
270+
)
271+
def test_candidate_ports_by_name(ports, names, expected):
272+
service = Service(id='svc', host='127.0.0.1', ports=ports)
273+
assert list(candidate_ports_by_name(service, names)) == expected
274+
275+
212276
def test_dev_placeholder_field_constants_match_models():
213277
"""Guard the one fact datadog_checks_dev must hand-copy from base.
214278

0 commit comments

Comments
 (0)