Skip to content

Commit 0dcaa61

Browse files
Add container-based config discovery support for haproxy (#24572)
* Add container-based config discovery support for haproxy Enables Autodiscovery to generate the OpenMetrics V2 instance config (port 8404, /metrics) from a discovered haproxy container's exposed ports. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Add changelog entry for haproxy discovery PR Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 2042232 commit 0dcaa61

9 files changed

Lines changed: 129 additions & 3 deletions

File tree

haproxy/assets/configuration/spec.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ name: HAProxy
22
fleet_configurable: true
33
files:
44
- name: haproxy.yaml
5+
discovery:
6+
strategies:
7+
- strategy: from_ports
8+
port_hints:
9+
- 8404
10+
candidates:
11+
- use_openmetrics: "true"
12+
openmetrics_endpoint: "http://{service.host}:{port.number}/metrics"
513
options:
614
- template: init_config
715
options:
@@ -237,3 +245,10 @@ files:
237245
port: 514
238246
service: haproxy
239247
source: haproxy
248+
- name: auto_conf.yaml
249+
options:
250+
- template: ad_identifiers
251+
overrides:
252+
value.example:
253+
- haproxy
254+
- template: auto_conf/discovery

haproxy/changelog.d/24572.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add container-based config discovery support.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# (C) Datadog, Inc. 2026-present
2+
# All rights reserved
3+
# Licensed under a 3-clause BSD style license (see LICENSE)
4+
5+
# This file is autogenerated.
6+
# To change this file you should edit assets/configuration/spec.yaml and then run the following commands:
7+
# ddev -x validate config -s <INTEGRATION_NAME>
8+
# ddev -x validate models -s <INTEGRATION_NAME>
9+
10+
from __future__ import annotations
11+
12+
from collections.abc import Iterator
13+
from typing import Any
14+
15+
from datadog_checks.base.utils.discovery import Service, candidate_ports
16+
from datadog_checks.haproxy.config_models import discovery_overrides
17+
from datadog_checks.haproxy.config_models.instance import InstanceConfig
18+
from datadog_checks.haproxy.config_models.shared import SharedConfig
19+
20+
21+
def _generated_candidates(service: Service) -> Iterator[dict[str, Any]]:
22+
shared = SharedConfig.model_validate({}, context={'configured_fields': frozenset()}).model_dump(
23+
by_alias=True, mode='json', exclude_none=True
24+
)
25+
# discovery[0]: from_ports
26+
for port in candidate_ports(service, [8404]):
27+
ctx = {'port': port}
28+
instance_data = {
29+
'use_openmetrics': 'true',
30+
'openmetrics_endpoint': 'http://{service.host}:{port.number}/metrics'.format(service=service, **ctx),
31+
}
32+
instance = InstanceConfig.model_validate(
33+
instance_data, context={'configured_fields': frozenset(instance_data)}
34+
).model_dump(by_alias=True, mode='json', exclude_none=True)
35+
yield {'init_config': shared, 'instances': [instance]}
36+
37+
38+
def candidates(service: Service) -> Iterator[dict[str, Any]]:
39+
override = getattr(discovery_overrides, 'candidates', None)
40+
if override is None:
41+
yield from _generated_candidates(service)
42+
else:
43+
yield from override(service, default=_generated_candidates)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# (C) Datadog, Inc. 2026-present
2+
# All rights reserved
3+
# Licensed under a 3-clause BSD style license (see LICENSE)
4+
5+
# Override the generated discovery candidates() for this integration.
6+
#
7+
# Define a candidates(service, default) function to wrap or replace the generated
8+
# candidate generation. `default` is the generated generator; call it to reuse
9+
# the spec-driven candidates, or ignore it to replace them entirely.
10+
#
11+
# def candidates(service, default):
12+
# yield from default(service)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# (C) Datadog, Inc. 2026-present
2+
# All rights reserved
3+
# Licensed under a 3-clause BSD style license (see LICENSE)
4+
5+
# Here you can define custom (local:) discovery strategies for this integration.
6+
#
7+
# Decorate a generator with @discovery_strategy (imported from
8+
# datadog_checks.base.utils.discovery) and reference it from the spec discovery
9+
# stanza as `strategy: local:<function_name>`. The function receives the
10+
# discovered Service plus the inputs declared in the spec and yields one context
11+
# (ctx) mapping per candidate, exposing the keys listed in `provides`.
12+
#
13+
# from datadog_checks.base.utils.discovery import discovery_strategy
14+
#
15+
# @discovery_strategy(provides=('svc',))
16+
# def from_some_config(service, config_path):
17+
# ...
18+
# yield {'svc': ...}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## @param ad_identifiers - list of strings - required
2+
## A list of container identifiers that are used by Autodiscovery to identify
3+
## which container the check should be run against. For more information, see:
4+
## https://docs.datadoghq.com/agent/guide/ad_identifiers/
5+
#
6+
ad_identifiers:
7+
- haproxy
8+
9+
## Enables configuration discovery
10+
#
11+
discovery: {}
12+
13+
## Unused init configuration
14+
#
15+
init_config:
16+
17+
## Unused instance configuration
18+
#
19+
instances: []

haproxy/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ classifiers = [
2828
"Private :: Do Not Upload",
2929
]
3030
dependencies = [
31-
"datadog-checks-base>=37.33.0",
31+
"datadog-checks-base>=37.41.0",
3232
]
3333
dynamic = [
3434
"version",

haproxy/tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import requests
1515
from packaging import version
1616

17-
from datadog_checks.dev import TempDir, WaitFor, docker_run
17+
from datadog_checks.dev import TempDir, WaitFor, docker_run, get_e2e_discovery_metadata
1818
from datadog_checks.haproxy import HAProxyCheck
1919
from datadog_checks.haproxy.metrics import METRIC_MAP
2020

@@ -49,7 +49,7 @@ def dd_environment():
4949
yield e
5050
else:
5151
with docker_run(compose_file=os.path.join(HERE, 'docker', 'haproxy.yaml'), endpoints=[ENDPOINT_PROMETHEUS]):
52-
yield INSTANCE
52+
yield INSTANCE, get_e2e_discovery_metadata()
5353

5454

5555
@pytest.fixture(scope='session')

haproxy/tests/test_e2e.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import pytest
55

66
from datadog_checks.base import is_affirmative
7+
from datadog_checks.dev.docker import assert_all_discovery_candidates_stable
78
from datadog_checks.dev.utils import get_metadata_metrics
9+
from datadog_checks.haproxy import HAProxyCheck
810

911
from .common import ENDPOINT_PROMETHEUS, HAPROXY_LEGACY, requires_new_environment
1012

@@ -40,3 +42,19 @@ def test_checkv2(dd_agent_check, instancev2, prometheus_metricsv2):
4042

4143
aggregator.assert_all_metrics_covered()
4244
aggregator.assert_metrics_using_metadata(get_metadata_metrics())
45+
46+
47+
def test_e2e_discovery(dd_agent_check_discovery, prometheus_metricsv2):
48+
aggregator = dd_agent_check_discovery(rate=True)
49+
50+
# discovery resolves the container's internal network address, which differs from
51+
# ENDPOINT_PROMETHEUS's host-mapped one, so the endpoint tag isn't asserted here.
52+
for metric in prometheus_metricsv2:
53+
aggregator.assert_metric('haproxy.{}'.format(metric))
54+
55+
aggregator.assert_all_metrics_covered()
56+
aggregator.assert_metrics_using_metadata(get_metadata_metrics())
57+
58+
59+
def test_e2e_discovery_all_candidates(dd_agent_check):
60+
assert_all_discovery_candidates_stable(dd_agent_check, HAProxyCheck)

0 commit comments

Comments
 (0)