Skip to content

Commit 366c2a2

Browse files
vitkyrkaclaude
andauthored
scylla: add container-based config discovery support (DataDog#24498)
* Add container-based config discovery support to scylla Adds a discovery strategy targeting the openmetrics_endpoint on port 9180 (scylla's Prometheus metrics port), generates auto_conf.yaml with ad_identifiers=[scylla], wires get_e2e_discovery_metadata() into the E2E fixture, and adds test_e2e_discovery/test_e2e_discovery_all_candidates tests to validate discovered configs against the real scylla-db container. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Add changelog entry for DataDog#24498 Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Tighten discovery stability log-pattern regex Anchor the benign-error exclusion to the specific "rpc - client <addr>: server connection dropped: connection is closed" log shape instead of a bare trailing substring match, per Codex review feedback, so an unrelated error line that happens to contain the same trailing text isn't silently excluded too. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * cleanup --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 12fbb16 commit 366c2a2

8 files changed

Lines changed: 138 additions & 5 deletions

File tree

scylla/assets/configuration/spec.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ name: Scylla
22
fleet_configurable: true
33
files:
44
- name: scylla.yaml
5+
discovery:
6+
strategies:
7+
- template: discovery/openmetrics_from_ports
8+
overrides:
9+
port_hints:
10+
- 9180
511
options:
612
- template: init_config
713
options:
@@ -64,3 +70,10 @@ files:
6470
- type: multi_line
6571
pattern: \d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])
6672
name: new_log_start_with_date
73+
- name: auto_conf.yaml
74+
options:
75+
- template: ad_identifiers
76+
overrides:
77+
value.example:
78+
- scylla
79+
- template: auto_conf/discovery

scylla/changelog.d/24498.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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.scylla.config_models import discovery_overrides
17+
from datadog_checks.scylla.config_models.instance import InstanceConfig
18+
from datadog_checks.scylla.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, [9180]):
27+
ctx = {'port': port}
28+
instance_data = {
29+
'openmetrics_endpoint': 'http://{service.host}:{port.number}/metrics'.format(service=service, **ctx),
30+
}
31+
instance = InstanceConfig.model_validate(
32+
instance_data, context={'configured_fields': frozenset(instance_data)}
33+
).model_dump(by_alias=True, mode='json', exclude_none=True)
34+
yield {'init_config': shared, 'instances': [instance]}
35+
36+
37+
def candidates(service: Service) -> Iterator[dict[str, Any]]:
38+
override = getattr(discovery_overrides, 'candidates', None)
39+
if override is None:
40+
yield from _generated_candidates(service)
41+
else:
42+
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+
- scylla
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: []

scylla/tests/conftest.py

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

9-
from datadog_checks.dev import docker_run, get_docker_hostname, get_here
9+
from datadog_checks.dev import docker_run, get_docker_hostname, get_e2e_discovery_metadata, get_here
1010

1111
HERE = get_here()
1212
HOST = get_docker_hostname()
@@ -20,7 +20,7 @@ def dd_environment(instance):
2020

2121
with docker_run(compose_file, log_patterns=[r'init - Scylla version \S* initialization completed.']):
2222
instances = {'instances': [instance]}
23-
yield instances
23+
yield instances, get_e2e_discovery_metadata()
2424

2525

2626
@pytest.fixture(scope="session")

scylla/tests/test_e2e.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66

77
import pytest
88

9+
from datadog_checks.dev.docker import CONTAINER_STABILITY_LOG_PATTERNS, assert_all_discovery_candidates_stable
910
from datadog_checks.scylla import ScyllaCheck
1011

1112
from .common import FLAKY_METRICS, INSTANCE_DEFAULT_METRICS_V2
1213

14+
# Probing scylla-db's internal storage RPC port (7000) with a plain HTTP GET
15+
# makes Scylla's own RPC layer log an ERROR ("rpc - client <addr>: server
16+
# connection dropped: connection is closed") once it rejects the unrecognized
17+
# protocol magic.
18+
DISCOVERY_STABILITY_LOG_PATTERNS = tuple(
19+
r'error(?!.*rpc - client \S+: server connection dropped: connection is closed)' if pattern == 'error' else pattern
20+
for pattern in CONTAINER_STABILITY_LOG_PATTERNS
21+
)
1322

14-
@pytest.mark.skipif(platform.python_version() < "3", reason='OpenMetrics V2 is only available with Python 3')
15-
def test_check_ok_omv2(dd_agent_check, instance):
16-
aggregator = dd_agent_check(instance, rate=True)
23+
24+
def assert_metrics(aggregator):
1725
for metric in INSTANCE_DEFAULT_METRICS_V2:
1826
if metric in FLAKY_METRICS:
1927
aggregator.assert_metric(metric, at_least=0)
@@ -22,3 +30,23 @@ def test_check_ok_omv2(dd_agent_check, instance):
2230

2331
aggregator.assert_all_metrics_covered()
2432
aggregator.assert_service_check('scylla.openmetrics.health', ScyllaCheck.OK)
33+
34+
35+
@pytest.mark.skipif(platform.python_version() < "3", reason='OpenMetrics V2 is only available with Python 3')
36+
def test_check_ok_omv2(dd_agent_check, instance):
37+
aggregator = dd_agent_check(instance, rate=True)
38+
assert_metrics(aggregator)
39+
40+
41+
@pytest.mark.skipif(platform.python_version() < "3", reason='OpenMetrics V2 is only available with Python 3')
42+
@pytest.mark.e2e
43+
def test_e2e_discovery(dd_agent_check_discovery):
44+
aggregator = dd_agent_check_discovery(rate=True)
45+
assert_metrics(aggregator)
46+
47+
48+
@pytest.mark.e2e
49+
def test_e2e_discovery_all_candidates(dd_agent_check):
50+
assert_all_discovery_candidates_stable(
51+
dd_agent_check, ScyllaCheck, compose_service='scylla-db', log_patterns=DISCOVERY_STABILITY_LOG_PATTERNS
52+
)

0 commit comments

Comments
 (0)