Skip to content

Commit f3bcc49

Browse files
authored
Update Node.js telemetry to use canonical names (#6536)
This adds both values as possible ones. That way there is an easy migration path.
1 parent d0050bf commit f3bcc49

5 files changed

Lines changed: 140 additions & 60 deletions

File tree

tests/appsec/test_asm_standalone.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -737,9 +737,17 @@ def test_telemetry_sca_enabled_propagated(self):
737737
payload.sort(key=lambda item: item["seq_id"], reverse=True)
738738
assert configuration_by_name
739739

740-
dd_appsec_sca_enabled = TelemetryUtils.get_dd_appsec_sca_enabled_str(context.library)
741-
742-
cfg_appsec_enabled = configuration_by_name.get(dd_appsec_sca_enabled)
740+
dd_appsec_sca_enabled_names = TelemetryUtils.get_dd_appsec_sca_enabled_names(context.library)
741+
dd_appsec_sca_enabled = " or ".join(dd_appsec_sca_enabled_names)
742+
743+
cfg_appsec_enabled = next(
744+
(
745+
configuration_by_name.get(config_name)
746+
for config_name in dd_appsec_sca_enabled_names
747+
if config_name in configuration_by_name
748+
),
749+
None,
750+
)
743751
assert cfg_appsec_enabled is not None, f"Missing telemetry config item for '{dd_appsec_sca_enabled}'"
744752

745753
outcome_value: bool | str = True

tests/docker_ssi/test_docker_ssi_appsec.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def test_telemetry_source_ssi(self):
4646

4747
# Check that instrumentation source is ssi
4848
injection_source = (
49-
configurations.get("DD_APPSEC_ENABLED") # Python
50-
or configurations.get("appsec.enabled") # Node.js & PHP
49+
configurations.get("DD_APPSEC_ENABLED") # Node.js & Python
50+
or configurations.get("appsec.enabled") # PHP
5151
or configurations.get("appsec_enabled") # Java
5252
)
5353
assert injection_source, f"instrumentation_source not found in configuration {configurations}"

tests/parametric/test_telemetry.py

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818
telemetry_name_mapping: dict[str, dict[str, str | list[str]]] = {
1919
"instrumentation_source": {
2020
"java": "DD_INSTRUMENTATION_SOURCE",
21+
"nodejs": ["instrumentationSource", "instrumentation_source"],
2122
},
2223
"ssi_injection_enabled": {
2324
"python": "DD_INJECTION_ENABLED",
2425
"java": "DD_INJECTION_ENABLED",
2526
"ruby": "DD_INJECTION_ENABLED",
27+
"nodejs": ["DD_INJECTION_ENABLED", "injectionEnabled", "ssi_injection_enabled"],
2628
"golang": ["DD_INJECTION_ENABLED", "injection_enabled"],
2729
},
2830
"ssi_forced_injection_enabled": {
2931
"python": "DD_INJECT_FORCE",
3032
"ruby": "DD_INJECT_FORCE",
3133
"java": "DD_INJECT_FORCE",
34+
"nodejs": ["DD_INJECT_FORCE", "injectForce", "ssi_forced_injection_enabled"],
3235
"golang": ["DD_INJECT_FORCE", "inject_force"],
3336
},
3437
"trace_sample_rate": {
@@ -41,7 +44,7 @@
4144
},
4245
"logs_injection_enabled": {
4346
"dotnet": "DD_LOGS_INJECTION",
44-
"nodejs": "DD_LOG_INJECTION", # TODO: rename to DD_LOGS_INJECTION in subsequent PR
47+
"nodejs": ["DD_LOGS_INJECTION", "DD_LOG_INJECTION"],
4548
"python": "DD_LOGS_INJECTION",
4649
"php": "trace.logs_enabled",
4750
"ruby": "DD_LOGS_INJECTION",
@@ -67,30 +70,30 @@
6770
"trace_enabled": {
6871
"dotnet": "DD_TRACE_ENABLED",
6972
"java": "DD_TRACE_ENABLED",
70-
"nodejs": "tracing",
73+
"nodejs": ["DD_TRACE_ENABLED", "tracing"],
7174
"python": "DD_TRACE_ENABLED",
7275
"ruby": "DD_TRACE_ENABLED",
7376
"golang": ["DD_TRACE_ENABLED", "trace_enabled"],
7477
},
7578
"profiling_enabled": {
7679
"dotnet": "DD_PROFILING_ENABLED",
77-
"nodejs": "profiling.enabled",
80+
"nodejs": ["DD_PROFILING_ENABLED", "profiling.enabled"],
7881
"python": "DD_PROFILING_ENABLED",
7982
"ruby": "DD_PROFILING_ENABLED",
8083
"golang": ["DD_PROFILING_ENABLED", "profiling_enabled"],
8184
"java": "DD_PROFILING_ENABLED",
8285
},
8386
"appsec_enabled": {
8487
"dotnet": "DD_APPSEC_ENABLED",
85-
"nodejs": "appsec.enabled",
88+
"nodejs": ["DD_APPSEC_ENABLED", "appsec.enabled"],
8689
"python": "DD_APPSEC_ENABLED",
8790
"ruby": "DD_APPSEC_ENABLED",
8891
"golang": ["DD_APPSEC_ENABLED", "appsec_enabled"],
8992
"java": "DD_APPSEC_ENABLED",
9093
},
9194
"data_streams_enabled": {
9295
"dotnet": "DD_DATA_STREAMS_ENABLED",
93-
"nodejs": "dsmEnabled",
96+
"nodejs": ["DD_DATA_STREAMS_ENABLED", "dsmEnabled"],
9497
"python": "DD_DATA_STREAMS_ENABLED",
9598
"java": "DD_DATA_STREAMS_ENABLED",
9699
"golang": ["DD_DATA_STREAMS_ENABLED", "data_streams_enabled"],
@@ -99,15 +102,15 @@
99102
"runtime_metrics_enabled": {
100103
"java": "DD_RUNTIME_METRICS_ENABLED",
101104
"dotnet": "DD_RUNTIME_METRICS_ENABLED",
102-
"nodejs": "runtime.metrics.enabled",
105+
"nodejs": ["DD_RUNTIME_METRICS_ENABLED", "runtime.metrics.enabled"],
103106
"python": "DD_RUNTIME_METRICS_ENABLED",
104107
"ruby": "DD_RUNTIME_METRICS_ENABLED",
105108
"golang": ["DD_RUNTIME_METRICS_ENABLED", "runtime_metrics_enabled"],
106109
},
107110
"dynamic_instrumentation_enabled": {
108111
"java": "DD_DYNAMIC_INSTRUMENTATION_ENABLED",
109112
"dotnet": "DD_DYNAMIC_INSTRUMENTATION_ENABLED",
110-
"nodejs": "dynamicInstrumentation.enabled",
113+
"nodejs": ["DD_DYNAMIC_INSTRUMENTATION_ENABLED", "dynamicInstrumentation.enabled"],
111114
"python": "DD_DYNAMIC_INSTRUMENTATION_ENABLED",
112115
"php": "dynamic_instrumentation.enabled",
113116
"ruby": "DD_DYNAMIC_INSTRUMENTATION_ENABLED",
@@ -162,16 +165,22 @@ def _check_propagation_style_with_inject_and_extract(
162165
"""
163166
# Define the inject and extract key names for each language
164167
if library_name in ("python", "ruby"):
165-
inject_key = "DD_TRACE_PROPAGATION_STYLE_INJECT"
166-
extract_key = "DD_TRACE_PROPAGATION_STYLE_EXTRACT"
168+
inject_keys = ["DD_TRACE_PROPAGATION_STYLE_INJECT"]
169+
extract_keys = ["DD_TRACE_PROPAGATION_STYLE_EXTRACT"]
167170
elif library_name == "nodejs":
168-
inject_key = "tracePropagationStyle.inject"
169-
extract_key = "tracePropagationStyle.extract"
171+
inject_keys = ["DD_TRACE_PROPAGATION_STYLE_INJECT", "tracePropagationStyle.inject"]
172+
extract_keys = ["DD_TRACE_PROPAGATION_STYLE_EXTRACT", "tracePropagationStyle.extract"]
170173
else:
171174
raise ValueError(f"Unsupported library for inject/extract propagation style: {library_name}")
172175

173176
# Check inject key
174-
inject_item = test_agent.get_telemetry_config_by_origin(configuration_by_name, inject_key, expected_origin)
177+
inject_item = None
178+
inject_key = inject_keys[0]
179+
for candidate in inject_keys:
180+
inject_item = test_agent.get_telemetry_config_by_origin(configuration_by_name, candidate, expected_origin)
181+
if inject_item is not None:
182+
inject_key = candidate
183+
break
175184
assert inject_item is not None, (
176185
f"No configuration found for '{inject_key}' with origin '{expected_origin}'. Full configuration_by_name: {configuration_by_name}"
177186
)
@@ -182,7 +191,13 @@ def _check_propagation_style_with_inject_and_extract(
182191
assert inject_item["value"], f"Expected non-empty value for '{inject_key}'"
183192

184193
# Check extract key
185-
extract_item = test_agent.get_telemetry_config_by_origin(configuration_by_name, extract_key, expected_origin)
194+
extract_item = None
195+
extract_key = extract_keys[0]
196+
for candidate in extract_keys:
197+
extract_item = test_agent.get_telemetry_config_by_origin(configuration_by_name, candidate, expected_origin)
198+
if extract_item is not None:
199+
extract_key = candidate
200+
break
186201
assert extract_item is not None, (
187202
f"No configuration found for '{extract_key}' with origin '{expected_origin}'. Full configuration_by_name: {configuration_by_name}"
188203
)
@@ -320,12 +335,9 @@ def test_library_settings(self, test_agent: TestAgentAPI, test_library: APMLibra
320335
)
321336
== "5.2.0"
322337
)
323-
assert (
324-
test_agent.get_telemetry_config_by_origin(
325-
configuration_by_name, "DD_TRACE_RATE_LIMIT", "env_var", return_value_only=True
326-
)
327-
== "10"
328-
)
338+
assert test_agent.get_telemetry_config_by_origin(
339+
configuration_by_name, "DD_TRACE_RATE_LIMIT", "env_var", return_value_only=True
340+
) in ("10", 10)
329341
assert (
330342
test_agent.get_telemetry_config_by_origin(
331343
configuration_by_name, "DD_TRACE_HEADER_TAGS", "env_var", return_value_only=True
@@ -1084,17 +1096,27 @@ def test_injection_enabled(
10841096
ssi_enabled_telemetry_names = _mapped_telemetry_name("ssi_injection_enabled")
10851097
inject_enabled = None
10861098
for ssi_name in ssi_enabled_telemetry_names:
1087-
inject_enabled = test_agent.get_telemetry_config_by_origin(
1088-
configuration_by_name, ssi_name, "env_var", fallback_to_first=(expected_value is None)
1089-
)
1099+
inject_enabled = test_agent.get_telemetry_config_by_origin(configuration_by_name, ssi_name, "env_var")
10901100
if inject_enabled is not None:
10911101
break
1102+
if inject_enabled is None and context.library == "nodejs":
1103+
for ssi_name in ssi_enabled_telemetry_names:
1104+
inject_enabled = test_agent.get_telemetry_config_by_origin(
1105+
configuration_by_name, ssi_name, "calculated", fallback_to_first=True
1106+
)
1107+
if inject_enabled is not None:
1108+
break
10921109
assert inject_enabled is not None, (
10931110
f"No configuration found for any of {' or '.join(ssi_enabled_telemetry_names)}"
10941111
)
10951112
assert isinstance(inject_enabled, dict)
1096-
assert inject_enabled.get("value") == expected_value
1097-
if expected_value is not None:
1113+
expected_values: tuple[object, ...] = (expected_value,)
1114+
if context.library == "nodejs":
1115+
expected_values += ([item.strip() for item in expected_value.split(",")],)
1116+
assert inject_enabled.get("value") in expected_values
1117+
# Node.js now derives the SSI source config from canonical config entries. Once PR #7734
1118+
# is fully rolled out, restore the strict env_var origin assertion here.
1119+
if expected_value is not None and context.library != "nodejs":
10981120
assert inject_enabled.get("origin") == "env_var"
10991121

11001122
@pytest.mark.parametrize(
@@ -1132,16 +1154,26 @@ def test_inject_force(self, expected_value: str, test_agent: TestAgentAPI, test_
11321154
inject_force = None
11331155
for inject_force_name in inject_force_telemetry_names:
11341156
inject_force = test_agent.get_telemetry_config_by_origin(
1135-
configuration_by_name, inject_force_name, "env_var", fallback_to_first=(expected_value == "none")
1157+
configuration_by_name, inject_force_name, "env_var"
11361158
)
11371159
if inject_force is not None:
11381160
break
1161+
if inject_force is None and context.library == "nodejs":
1162+
for inject_force_name in inject_force_telemetry_names:
1163+
inject_force = test_agent.get_telemetry_config_by_origin(
1164+
configuration_by_name, inject_force_name, "calculated", fallback_to_first=True
1165+
)
1166+
if inject_force is not None:
1167+
break
11391168
assert inject_force is not None, (
11401169
f"No configuration found for any of {' or '.join(inject_force_telemetry_names)}"
11411170
)
11421171
assert isinstance(inject_force, dict)
11431172
assert str(inject_force.get("value")).lower() == expected_value
1144-
assert inject_force.get("origin") == "env_var"
1173+
# Node.js now derives the SSI source config from canonical config entries. Once PR #7734
1174+
# is fully rolled out, restore the strict env_var origin assertion here.
1175+
if context.library != "nodejs":
1176+
assert inject_force.get("origin") == "env_var"
11451177

11461178
@pytest.mark.parametrize("library_env", [{**DEFAULT_ENVVARS, "DD_SERVICE": "service_test"}])
11471179
def test_instrumentation_source_non_ssi(self, test_agent: TestAgentAPI, test_library: APMLibrary):
@@ -1161,6 +1193,15 @@ def test_instrumentation_source_non_ssi(self, test_agent: TestAgentAPI, test_lib
11611193
)
11621194
if instrumentation_source is not None:
11631195
break
1196+
# Node.js reports instrumentationSource as a calculated value with the new config pipeline.
1197+
# Remove this fallback after PR #7734 lands and older values no longer need coverage.
1198+
if instrumentation_source is None and context.library == "nodejs":
1199+
for instrumentation_source_name in instrumentation_source_telemetry_names:
1200+
instrumentation_source = test_agent.get_telemetry_config_by_origin(
1201+
configuration_by_name, instrumentation_source_name, "calculated", fallback_to_first=True
1202+
)
1203+
if instrumentation_source is not None:
1204+
break
11641205
assert instrumentation_source is not None, (
11651206
f"No configuration found for any of {' or '.join(instrumentation_source_telemetry_names)}"
11661207
)
@@ -1217,15 +1258,23 @@ def _assert_telemetry_sca_enabled_propagated(
12171258
):
12181259
assert test_library.is_alive(), "Library container is not running"
12191260
configuration_by_name = test_agent.wait_for_telemetry_configurations()
1220-
dd_appsec_sca_enabled = TelemetryUtils.get_dd_appsec_sca_enabled_str(context.library)
1261+
dd_appsec_sca_enabled_names = TelemetryUtils.get_dd_appsec_sca_enabled_names(context.library)
1262+
dd_appsec_sca_enabled = " or ".join(dd_appsec_sca_enabled_names)
12211263

12221264
logger.info(f"""Check that:
12231265
* the env var DD_APPSEC_SCA_ENABLED={library_env["DD_APPSEC_SCA_ENABLED"]}
12241266
* is reported in telemetry configuration {dd_appsec_sca_enabled} as value={outcome_value}""")
12251267

12261268
assert configuration_by_name is not None, "Missing telemetry configuration"
12271269

1228-
cfg_appsec_enabled = configuration_by_name.get(dd_appsec_sca_enabled)
1270+
cfg_appsec_enabled = next(
1271+
(
1272+
configuration_by_name.get(config_name)
1273+
for config_name in dd_appsec_sca_enabled_names
1274+
if config_name in configuration_by_name
1275+
),
1276+
None,
1277+
)
12291278
logger.info(f"Oberved {dd_appsec_sca_enabled}: {cfg_appsec_enabled}")
12301279
assert cfg_appsec_enabled is not None, f"Missing telemetry config item for '{dd_appsec_sca_enabled}'"
12311280

@@ -1239,11 +1288,19 @@ def test_telemetry_sca_enabled_not_propagated(self, test_agent: TestAgentAPI, te
12391288

12401289
assert configuration_by_name is not None, "Missing telemetry configuration"
12411290

1242-
dd_appsec_sca_enabled = TelemetryUtils.get_dd_appsec_sca_enabled_str(context.library)
1291+
dd_appsec_sca_enabled_names = TelemetryUtils.get_dd_appsec_sca_enabled_names(context.library)
1292+
dd_appsec_sca_enabled = " or ".join(dd_appsec_sca_enabled_names)
12431293

12441294
if context.library in ("java", "nodejs", "python", "ruby"):
1245-
cfg_appsec_enabled = configuration_by_name.get(dd_appsec_sca_enabled)
1295+
cfg_appsec_enabled = next(
1296+
(
1297+
configuration_by_name.get(config_name)
1298+
for config_name in dd_appsec_sca_enabled_names
1299+
if config_name in configuration_by_name
1300+
),
1301+
None,
1302+
)
12461303
assert cfg_appsec_enabled is not None, f"Missing telemetry config item for '{dd_appsec_sca_enabled}'"
12471304
assert cfg_appsec_enabled[0].get("value") is None
12481305
else:
1249-
assert dd_appsec_sca_enabled not in configuration_by_name
1306+
assert all(config_name not in configuration_by_name for config_name in dd_appsec_sca_enabled_names)

0 commit comments

Comments
 (0)