diff --git a/manifests/cpp.yml b/manifests/cpp.yml index bf39d60399a..6b31fc0f054 100644 --- a/manifests/cpp.yml +++ b/manifests/cpp.yml @@ -160,6 +160,7 @@ tests/: Test_Config_TraceEnabled: v1.0.1.dev Test_Config_TraceLogDirectory: missing_feature Test_Config_UnifiedServiceTagging: v1.0.1.dev + Test_Stable_Config_Default: missing_feature test_crashtracking.py: missing_feature test_dynamic_configuration.py: TestDynamicConfigV1_EmptyServiceTargets: missing_feature diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index 3599dd01acb..973eed9a75c 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -421,6 +421,7 @@ tests/: Test_Config_TraceEnabled: v3.3.0 Test_Config_TraceLogDirectory: v3.3.0 Test_Config_UnifiedServiceTagging: v3.3.0 + Test_Stable_Config_Default: missing_feature test_crashtracking.py: Test_Crashtracking: v3.2.0 test_dynamic_configuration.py: diff --git a/manifests/golang.yml b/manifests/golang.yml index 5b8c24e73a0..c9eef846d1d 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -553,6 +553,7 @@ tests/: Test_Config_TraceEnabled: v1.67.0 Test_Config_TraceLogDirectory: v1.70.0 Test_Config_UnifiedServiceTagging: bug (APMAPI-746) + Test_Stable_Config_Default: missing_feature test_crashtracking.py: missing_feature test_dynamic_configuration.py: TestDynamicConfigSamplingRules: v1.64.0 diff --git a/manifests/java.yml b/manifests/java.yml index 8d1aab57d07..d5f3d29101a 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -1617,6 +1617,7 @@ tests/: Test_Config_TraceEnabled: v1.39.0 Test_Config_TraceLogDirectory: missing_feature Test_Config_UnifiedServiceTagging: v1.41.1 + Test_Stable_Config_Default: missing_feature test_crashtracking.py: Test_Crashtracking: v1.38.0 test_dynamic_configuration.py: diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index a3f8c1de909..6f6a705a839 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -809,6 +809,7 @@ tests/: Test_Config_TraceEnabled: *ref_4_3_0 Test_Config_TraceLogDirectory: missing_feature Test_Config_UnifiedServiceTagging: *ref_5_25_0 + Test_Stable_Config_Default: missing_feature test_crashtracking.py: Test_Crashtracking: *ref_5_27_0 test_dynamic_configuration.py: diff --git a/manifests/php.yml b/manifests/php.yml index 9800e5def28..0120cffa176 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -396,6 +396,7 @@ tests/: Test_Config_TraceEnabled: v1.3.0 # Unknown initial version Test_Config_TraceLogDirectory: missing_feature Test_Config_UnifiedServiceTagging: v1.5.0 + Test_Stable_Config_Default: missing_feature test_crashtracking.py: Test_Crashtracking: v1.3.0 test_dynamic_configuration.py: diff --git a/manifests/python.yml b/manifests/python.yml index 349ac2565ab..b12f8d60ee3 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -781,6 +781,7 @@ tests/: Test_Config_TraceEnabled: v2.12.2 Test_Config_TraceLogDirectory: missing_feature Test_Config_UnifiedServiceTagging: v2.12.2 + Test_Stable_Config_Default: missing_feature test_crashtracking.py: Test_Crashtracking: v2.11.2 test_dynamic_configuration.py: diff --git a/manifests/ruby.yml b/manifests/ruby.yml index e3c9794e59f..747a39ec287 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -419,6 +419,7 @@ tests/: Test_Config_TraceEnabled: v2.0.0 Test_Config_TraceLogDirectory: missing_feature Test_Config_UnifiedServiceTagging: v2.5.0 + Test_Stable_Config_Default: missing_feature test_crashtracking.py: Test_Crashtracking: v2.3.0 test_dynamic_configuration.py: diff --git a/tests/parametric/test_config_consistency.py b/tests/parametric/test_config_consistency.py index 861c8291416..2837b2c13b0 100644 --- a/tests/parametric/test_config_consistency.py +++ b/tests/parametric/test_config_consistency.py @@ -6,6 +6,7 @@ import pytest from utils import scenarios, features, context, missing_feature, irrelevant, flaky, bug from utils.parametric.spec.trace import find_span_in_traces, find_only_span +import os parametrize = pytest.mark.parametrize @@ -386,3 +387,38 @@ def test_dogstatsd_custom_port(self, library_env, test_agent, test_library): with test_library as t: resp = t.config() assert resp["dd_dogstatsd_port"] == "8150" + + +@scenarios.parametric +@features.stable_configuration_support +class Test_Stable_Config_Default: + """Verify that stable config works as intended""" + + @missing_feature( + context.library in ["ruby", "cpp", "dotnet", "golang", "java", "nodejs", "php", "python"], + reason="does not support stable configurations yet", + ) + @pytest.mark.parametrize("library_env", [{"STABLE_CONFIG_SELECTOR": "true", "DD_SERVICE": "not-my-service"}]) + def test_config_stable(self, library_env, test_agent, test_library): + path = "/etc/datadog-agent/managed/datadog-apm-libraries/stable/libraries_config.yaml" + stable_config = """ +rules: + - selectors: + - origin: environment_variables + matches: + - STABLE_CONFIG_SELECTOR=true + operator: equals + configuration: + DD_SERVICE: my-service +""" + + with test_library: + success, message = test_library.container_exec_run( + f"bash -c \"mkdir -p {os.path.dirname(path)} && printf '{stable_config}' | tee {path}\"" + ) + assert success, message + test_library.container_restart() + config = test_library.config() + assert ( + config["dd_service"] == "my-service" + ), f"Service name is '{config["dd_service"]}' instead of 'my-service'" diff --git a/utils/_features.py b/utils/_features.py index 2ad70b76d00..baf77fddf4d 100644 --- a/utils/_features.py +++ b/utils/_features.py @@ -2390,5 +2390,14 @@ def otel_propagators_api(test_object): pytest.mark.features(feature_id=361)(test_object) return test_object + @staticmethod + def stable_configuration_support(test_object): + """Enforces that basic stable configuration support exists + + https://feature-parity.us1.prod.dog/#/?feature=365 + """ + pytest.mark.features(feature_id=365)(test_object) + return test_object + features = _Features() diff --git a/utils/parametric/_library_client.py b/utils/parametric/_library_client.py index 1689ed55a67..3df25ccb0cf 100644 --- a/utils/parametric/_library_client.py +++ b/utils/parametric/_library_client.py @@ -49,6 +49,7 @@ def __init__(self, url: str, timeout: int, container: Container): self._base_url = url self._session = requests.Session() self.container = container + self.timeout = timeout # wait for server to start self._wait(timeout) @@ -70,6 +71,10 @@ def _wait(self, timeout): message = f"Timeout of {timeout} seconds exceeded waiting for HTTP server to start. Please check logs." _fail(message) + def container_restart(self): + self.container.restart() + self._wait(self.timeout) + def is_alive(self) -> bool: self.container.reload() return ( @@ -96,16 +101,16 @@ def crash(self) -> None: def container_exec_run(self, command: str) -> tuple[bool, str]: try: - code, (stdout, _) = self.container.exec_run(command, demux=True) + code, (stdout, stderr) = self.container.exec_run(command, demux=True) if code is None: success = False message = "Exit code from command in the parametric app container is None" - elif stdout is None: + elif stderr is not None or code != 0: success = False - message = "Stdout from command in the parametric app container is None" + message = f"Error code {code}: {stderr.decode()}" else: success = True - message = stdout.decode() + message = stdout.decode() if stdout is not None else "" except BaseException: return False, "Encountered an issue running command in the parametric app container" @@ -426,6 +431,9 @@ def crash(self) -> None: def container_exec_run(self, command: str) -> tuple[bool, str]: return self._client.container_exec_run(command) + def container_restart(self): + self._client.container_restart() + @contextlib.contextmanager def dd_start_span( self,