Skip to content

Commit 59526bb

Browse files
authored
Test Stable Config support for extended configs (#5357)
1 parent 5d7bba2 commit 59526bb

File tree

10 files changed

+259
-12
lines changed

10 files changed

+259
-12
lines changed

manifests/cpp.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ tests/:
1919
Test_Config_TraceLogDirectory: missing_feature
2020
Test_Config_UnifiedServiceTagging: ">1.0.0"
2121
Test_Stable_Config_Default: missing_feature
22+
Test_Stable_Config_Rules: missing_feature
2223
test_crashtracking.py: missing_feature
2324
test_dynamic_configuration.py:
2425
TestDynamicConfigV1_EmptyServiceTargets: ">1.0.0"

manifests/dotnet.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ tests/:
557557
Test_Config_TraceLogDirectory: v3.3.0
558558
Test_Config_UnifiedServiceTagging: v3.3.0
559559
Test_Stable_Config_Default: v3.28.0
560+
Test_Stable_Config_Rules: missing_feature
560561
test_crashtracking.py:
561562
Test_Crashtracking: v3.2.0
562563
test_dynamic_configuration.py:

manifests/golang.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ tests/:
756756
Test_Config_TraceLogDirectory: v1.70.0
757757
Test_Config_UnifiedServiceTagging: v1.72.0
758758
Test_Stable_Config_Default: v2.1.0-dev.2
759+
Test_Stable_Config_Rules: missing_feature
759760
test_crashtracking.py: missing_feature
760761
test_dynamic_configuration.py:
761762
TestDynamicConfigSamplingRules: v1.64.0

manifests/java.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,7 @@ tests/:
20992099
Test_Config_TraceLogDirectory: missing_feature
21002100
Test_Config_UnifiedServiceTagging: v1.41.1
21012101
Test_Stable_Config_Default: v1.49.0-SNAPSHOT
2102+
Test_Stable_Config_Rules: v1.49.0-SNAPSHOT
21022103
test_crashtracking.py:
21032104
Test_Crashtracking: v1.38.0
21042105
test_dynamic_configuration.py:

manifests/nodejs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,7 @@ tests/:
12981298
Test_Config_TraceLogDirectory: missing_feature
12991299
Test_Config_UnifiedServiceTagging: *ref_5_25_0
13001300
Test_Stable_Config_Default: *ref_5_62_0
1301+
Test_Stable_Config_Rules: missing_feature
13011302
test_crashtracking.py:
13021303
Test_Crashtracking: *ref_5_27_0
13031304
test_dynamic_configuration.py:

manifests/php.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ tests/:
559559
Test_Config_TraceLogDirectory: missing_feature
560560
Test_Config_UnifiedServiceTagging: v1.5.0
561561
Test_Stable_Config_Default: v1.8.0
562+
Test_Stable_Config_Rules: missing_feature
562563
test_crashtracking.py:
563564
Test_Crashtracking: v1.3.0
564565
test_dynamic_configuration.py:

manifests/python.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,7 @@ tests/:
10641064
Test_Config_TraceLogDirectory: missing_feature
10651065
Test_Config_UnifiedServiceTagging: v2.12.2
10661066
Test_Stable_Config_Default: v3.12.0.dev # default value of log injection changed in v3.12.0
1067+
Test_Stable_Config_Rules: missing_feature
10671068
test_crashtracking.py:
10681069
Test_Crashtracking: v2.11.2
10691070
test_dynamic_configuration.py:

manifests/ruby.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ tests/:
735735
Test_Config_TraceLogDirectory: missing_feature
736736
Test_Config_UnifiedServiceTagging: v2.5.0
737737
Test_Stable_Config_Default: v2.18.0
738+
Test_Stable_Config_Rules: missing_feature
738739
test_crashtracking.py:
739740
Test_Crashtracking: v2.3.0
740741
test_dynamic_configuration.py:

tests/parametric/test_config_consistency.py

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ class CustomDumper(yaml.Dumper):
428428
@features.stable_configuration_support
429429
@rfc("https://docs.google.com/document/d/1MNI5d3g6R8uU3FEWf2e08aAsFcJDVhweCPMjQatEb0o")
430430
class Test_Stable_Config_Default(StableConfigWriter):
431-
"""Verify that stable config works as intended"""
431+
"""Verify that stable config works as intended (apm_configuration_default)"""
432432

433433
@pytest.mark.parametrize("library_env", [{}])
434434
@pytest.mark.parametrize(
@@ -505,7 +505,60 @@ def test_default_config(
505505
config = test_library.config()
506506
assert expected.items() <= config.items()
507507

508-
# @pytest.mark.parametrize("library_env", [{}])
508+
@pytest.mark.parametrize("library_env", [{}])
509+
@pytest.mark.parametrize(
510+
("name", "apm_configuration_default", "expected"),
511+
[
512+
(
513+
"tags",
514+
{"DD_TAGS": "tag1:value1,tag2:value2"},
515+
{
516+
"dd_tags": ["tag1:value1", "tag2:value2"]
517+
if context.library in ["dotnet", "php"]
518+
else "tag1:value1,tag2:value2"
519+
},
520+
),
521+
(
522+
"128bit_traceids",
523+
{"DD_TRACE_PROPAGATION_STYLE": "tracecontext"},
524+
{"dd_trace_propagation_style": "tracecontext"},
525+
),
526+
],
527+
ids=lambda name: name,
528+
)
529+
@pytest.mark.parametrize(
530+
"path",
531+
[
532+
"/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml",
533+
"/etc/datadog-agent/application_monitoring.yaml",
534+
],
535+
)
536+
@missing_feature(
537+
context.library in ["cpp", "golang", "nodejs"],
538+
reason="extended configs are not supported",
539+
)
540+
def test_extended_configs(
541+
self, test_agent, test_library, path, library_env, name, apm_configuration_default, expected
542+
):
543+
"""Test that SDKs support extended configuration options beyond just product enablement.
544+
545+
This test uses representative configurations (tags, propagation style) to verify that
546+
stable config supports more than just the basic product enablement configs tested
547+
in test_default_config. It ensures SDKs can handle complex configuration values
548+
like tag arrays and propagation style settings through the stable config mechanism.
549+
"""
550+
with test_library:
551+
self.write_stable_config(
552+
{
553+
"apm_configuration_default": apm_configuration_default,
554+
},
555+
path,
556+
test_library,
557+
)
558+
test_library.container_restart()
559+
config = test_library.config()
560+
assert expected.items() <= config.items(), f"Expected config items not found. Actual config is: {config}"
561+
509562
@pytest.mark.parametrize(
510563
"test",
511564
[
@@ -627,12 +680,15 @@ def test_config_precedence(self, name, test_agent, test_library, local_cfg, libr
627680
"unexpected values for the following configurations: {}"
628681
).format([k for k in config.keys() & expected.keys() if config[k] != expected[k]])
629682

683+
684+
@scenarios.parametric
685+
@features.stable_configuration_support
686+
@rfc("https://docs.google.com/document/d/1MNI5d3g6R8uU3FEWf2e08aAsFcJDVhweCPMjQatEb0o")
687+
class Test_Stable_Config_Rules(StableConfigWriter):
688+
"""Verify that stable config targeting rules work as intended (apm_configuration_rules)"""
689+
630690
@pytest.mark.parametrize("library_env", [{"STABLE_CONFIG_SELECTOR": "true", "DD_SERVICE": "not-my-service"}])
631-
@missing_feature(
632-
context.library in ["ruby", "cpp", "dotnet", "golang", "nodejs", "php", "python"],
633-
reason="UST stable config is phase 2",
634-
)
635-
def test_config_stable(self, library_env, test_agent, test_library):
691+
def test_targeting_rules(self, library_env, test_agent, test_library):
636692
path = "/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml"
637693
with test_library:
638694
self.write_stable_config(
@@ -660,10 +716,6 @@ def test_config_stable(self, library_env, test_agent, test_library):
660716
config["dd_service"] == "my-service"
661717
), f"Service name is '{config["dd_service"]}' instead of 'my-service'"
662718

663-
@missing_feature(
664-
context.library in ["ruby", "cpp", "dotnet", "golang", "nodejs", "php", "python"],
665-
reason="UST stable config is phase 2",
666-
)
667719
@pytest.mark.parametrize(
668720
"library_extra_command_arguments",
669721
[

tests/parametric/test_telemetry.py

Lines changed: 188 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from .conftest import StableConfigWriter, _TestAgentAPI
1111
from utils.telemetry_utils import TelemetryUtils
12-
from utils import context, scenarios, rfc, features, missing_feature, irrelevant, logger
12+
from utils import context, scenarios, rfc, features, missing_feature, irrelevant, logger, bug
1313

1414

1515
telemetry_name_mapping = {
@@ -82,6 +82,15 @@
8282
"ruby": "DD_TRACE_DEBUG",
8383
"python": "DD_TRACE_DEBUG",
8484
},
85+
"tags": {
86+
"java": "trace_tags",
87+
"dotnet": "DD_TAGS",
88+
"python": "DD_TAGS",
89+
},
90+
"trace_propagation_style": {
91+
"dotnet": "DD_TRACE_PROPAGATION_STYLE",
92+
"php": "trace.propagation_style",
93+
},
8594
}
8695

8796

@@ -105,6 +114,48 @@ def _find_configuration_by_origin(config_list: list[dict], origin: str) -> dict
105114
return None
106115

107116

117+
def _check_propagation_style_with_inject_and_extract(
118+
test_agent, configuration_by_name: dict, expected_origin: str, library_name: str
119+
) -> None:
120+
"""Check both inject and extract propagation style keys for languages that report them separately.
121+
122+
Some libraries report propagation style using separate inject and extract keys
123+
instead of a single combined key. This function validates that both keys exist with the
124+
expected origin and have non-empty values.
125+
126+
Raises an AssertionError if either key is missing, has wrong origin, or has empty value
127+
"""
128+
# Define the inject and extract key names for each language
129+
if library_name == "python":
130+
inject_key = "DD_TRACE_PROPAGATION_STYLE_INJECT"
131+
extract_key = "DD_TRACE_PROPAGATION_STYLE_EXTRACT"
132+
elif library_name == "ruby":
133+
inject_key = "tracing.propagation_style_inject"
134+
extract_key = "tracing.propagation_style_extract"
135+
else:
136+
raise ValueError(f"Unsupported library for inject/extract propagation style: {library_name}")
137+
138+
# Check inject key
139+
inject_item = test_agent.get_telemetry_config_by_origin(configuration_by_name, inject_key, expected_origin)
140+
assert (
141+
inject_item is not None
142+
), f"No configuration found for '{inject_key}' with origin '{expected_origin}'. Full configuration_by_name: {configuration_by_name}"
143+
assert (
144+
inject_item["origin"] == expected_origin
145+
), f"Origin mismatch for {inject_item}. Expected origin: '{expected_origin}', Actual origin: '{inject_item.get('origin', '<missing>')}'"
146+
assert inject_item["value"], f"Expected non-empty value for '{inject_key}'"
147+
148+
# Check extract key
149+
extract_item = test_agent.get_telemetry_config_by_origin(configuration_by_name, extract_key, expected_origin)
150+
assert (
151+
extract_item is not None
152+
), f"No configuration found for '{extract_key}' with origin '{expected_origin}'. Full configuration_by_name: {configuration_by_name}"
153+
assert (
154+
extract_item["origin"] == expected_origin
155+
), f"Origin mismatch for {extract_item}. Expected origin: '{expected_origin}', Actual origin: '{extract_item.get('origin', '<missing>')}'"
156+
assert extract_item["value"], f"Expected non-empty value for '{extract_key}'"
157+
158+
108159
@scenarios.parametric
109160
@rfc("https://docs.google.com/document/d/1In4TfVBbKEztLzYg4g0si5H56uzAbYB3OfqzRGP2xhg/edit")
110161
@features.telemetry_app_started_event
@@ -690,6 +741,142 @@ def test_stable_configuration_config_id(
690741
assert telemetry_item["origin"] == "local_stable_config"
691742
assert "config_id" not in telemetry_item or telemetry_item["config_id"] is None
692743

744+
@pytest.mark.parametrize(
745+
("local_cfg", "library_env", "fleet_cfg", "expected_origins"),
746+
[
747+
(
748+
{
749+
"DD_TRACE_PROPAGATION_STYLE": "tracecontext",
750+
"DD_TAGS": "tag1:value1,tag2:value2",
751+
},
752+
{
753+
"DD_TELEMETRY_HEARTBEAT_INTERVAL": "0.1", # Decrease the heartbeat/poll intervals to speed up the tests
754+
},
755+
{
756+
"DD_TRACE_PROPAGATION_STYLE": "datadog",
757+
},
758+
{
759+
"tags": "local_stable_config",
760+
"trace_propagation_style": "fleet_stable_config",
761+
},
762+
)
763+
],
764+
)
765+
@missing_feature(
766+
context.library in ["cpp", "golang", "nodejs"],
767+
reason="extended configs are not supported",
768+
)
769+
@bug(context.library == "python", reason="APMAPI-1630")
770+
@bug(context.library == "ruby", reason="APMAPI-1631")
771+
def test_stable_configuration_origin_extended_configs_good_use_case(
772+
self, local_cfg, library_env, fleet_cfg, test_agent, test_library, expected_origins
773+
):
774+
"""Test that extended configuration options (tags, propagation style) report their origin correctly.
775+
776+
This test verifies that complex configuration values like tag arrays and propagation
777+
styles are properly tracked with their configuration origin (local vs fleet stable config).
778+
"""
779+
with test_library:
780+
self.write_stable_config(
781+
{
782+
"apm_configuration_default": local_cfg,
783+
},
784+
"/etc/datadog-agent/application_monitoring.yaml",
785+
test_library,
786+
)
787+
self.write_stable_config(
788+
{
789+
"apm_configuration_default": fleet_cfg,
790+
},
791+
"/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml",
792+
test_library,
793+
)
794+
# Sleep between telemetry events to ensure they are recorded with different timestamps, to later reorder them.
795+
# seq_id can't be used to sort because payloads are sent from different tracer sessions.
796+
time.sleep(1)
797+
test_library.container_restart()
798+
test_library.dd_start_span("test")
799+
configuration_by_name = test_agent.wait_for_telemetry_configurations()
800+
for cfg_name, expected_origin in expected_origins.items():
801+
apm_telemetry_name = _mapped_telemetry_name(context, cfg_name)
802+
telemetry_item = test_agent.get_telemetry_config_by_origin(
803+
configuration_by_name, apm_telemetry_name, expected_origin
804+
)
805+
assert (
806+
telemetry_item is not None
807+
), f"No configuration found for '{apm_telemetry_name}' with origin '{expected_origin}'. Full configuration_by_name: {configuration_by_name}"
808+
809+
actual_origin = telemetry_item.get("origin", "<missing>")
810+
assert (
811+
telemetry_item["origin"] == expected_origin
812+
), f"Origin mismatch for {telemetry_item}. Expected origin: '{expected_origin}', Actual origin: '{actual_origin}'"
813+
assert telemetry_item["value"]
814+
815+
@pytest.mark.parametrize(
816+
("local_cfg", "library_env", "fleet_cfg", "expected_origins"),
817+
[
818+
(
819+
{
820+
"DD_TRACE_PROPAGATION_STYLE": "tracecontext",
821+
"DD_TAGS": "tag1:value1,tag2:value2",
822+
},
823+
{
824+
"DD_TELEMETRY_HEARTBEAT_INTERVAL": "0.1", # Decrease the heartbeat/poll intervals to speed up the tests
825+
},
826+
{
827+
"DD_TRACE_PROPAGATION_STYLE": "datadog",
828+
},
829+
{
830+
"tags": "local_stable_config",
831+
"trace_propagation_style": "fleet_stable_config",
832+
},
833+
)
834+
],
835+
)
836+
@missing_feature(
837+
context.library in ["cpp", "golang", "nodejs"],
838+
reason="extended configs are not supported",
839+
)
840+
@bug(context.library == "ruby", reason="APMAPI-1650")
841+
@irrelevant(context.library in ["java", "php", "dotnet"], reason="temporary use case for python and ruby")
842+
def test_stable_configuration_origin_extended_configs_temporary_use_case(
843+
self, local_cfg, library_env, fleet_cfg, test_agent, test_library, expected_origins
844+
):
845+
"""Test that extended configuration options (tags, propagation style) report their origin correctly.
846+
847+
This test verifies that complex configuration values like tag arrays and propagation
848+
styles are properly tracked with their configuration origin (local vs fleet stable config).
849+
"""
850+
with test_library:
851+
self.write_stable_config(
852+
{
853+
"apm_configuration_default": local_cfg,
854+
},
855+
"/etc/datadog-agent/application_monitoring.yaml",
856+
test_library,
857+
)
858+
self.write_stable_config(
859+
{
860+
"apm_configuration_default": fleet_cfg,
861+
},
862+
"/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml",
863+
test_library,
864+
)
865+
# Sleep between telemetry events to ensure they are recorded with different timestamps, to later reorder them.
866+
# seq_id can't be used to sort because payloads are sent from different tracer sessions.
867+
time.sleep(1)
868+
test_library.container_restart()
869+
test_library.dd_start_span("test")
870+
configuration_by_name = test_agent.wait_for_telemetry_configurations()
871+
for cfg_name, expected_origin in expected_origins.items():
872+
# Python and Ruby only report inject and extract keys for trace_propagation_style
873+
if cfg_name == "trace_propagation_style" and context.library.name in ["python", "ruby"]:
874+
_check_propagation_style_with_inject_and_extract(
875+
test_agent, configuration_by_name, expected_origin, context.library.name
876+
)
877+
if cfg_name == "tags" and context.library.name in ["ruby"]:
878+
continue
879+
693880

694881
DEFAULT_ENVVARS = {
695882
# Decrease the heartbeat/poll intervals to speed up the tests

0 commit comments

Comments
 (0)