diff --git a/ddtrace/internal/core/crashtracking.py b/ddtrace/internal/core/crashtracking.py index 253ab133bc9..02505672ec8 100644 --- a/ddtrace/internal/core/crashtracking.py +++ b/ddtrace/internal/core/crashtracking.py @@ -19,7 +19,6 @@ from ddtrace.internal.settings.crashtracker import config as crashtracker_config from ddtrace.internal.settings.profiling import config as profiling_config from ddtrace.internal.settings.profiling import config_str -from ddtrace.internal.telemetry.writer import _agentless_endpoint_url log = get_logger(__name__) @@ -125,17 +124,12 @@ def _get_args(additional_tags: Optional[dict[str, str]]): log.error("Invalid stacktrace_resolver value: %s", crashtracker_config.stacktrace_resolver) stacktrace_resolver = StacktraceCollection.EnabledWithInprocessSymbols - # Crash reports ride the telemetry intake, so agentless points at the same host the telemetry - # writer uses. libdatadog only resolves the direct intake path (rather than the agent's - # telemetry proxy path) when the endpoint carries an API key and direct submission is - # enabled in the receiver process, so both are set together below. + # Do not manually compute an url. The crashtracker receiver has this handling, + # given DD_API_KEY and DD_SITE. Nothing to do for us here. + # We even cannot do so, otherwise we'll pin crashtracking to a single host, + # instead of dedicated error-reporting and crashtracking hosts. crash_agentless = config._agentless_enabled and config._dd_api_key - if crash_agentless: - upload_url = _agentless_endpoint_url(config._dd_site) - api_key = config._dd_api_key - else: - upload_url = agent_config.trace_agent_url - api_key = None + upload_url = None if crash_agentless else agent_config.trace_agent_url # Create crashtracker configuration crashtracker_configuration = CrashtrackerConfiguration( @@ -149,13 +143,15 @@ def _get_args(additional_tags: Optional[dict[str, str]]): crashtracker_config.debug_url or upload_url, None, # unix_socket_path crashtracker_config._test_token, - api_key, + None, ) receiver_env = {} if crash_agentless: receiver_env["_DD_DIRECT_SUBMISSION_ENABLED"] = "true" + receiver_env["DD_API_KEY"] = config._dd_api_key + receiver_env["DD_SITE"] = config._dd_site # Don't pass all env vars to the receiver process, because there are # conflicts with export location derivation diff --git a/releasenotes/notes/agentless-crashtracking-fix-endpoint-e3e4b73aad92c90f.yaml b/releasenotes/notes/agentless-crashtracking-fix-endpoint-e3e4b73aad92c90f.yaml new file mode 100644 index 00000000000..71891bb2791 --- /dev/null +++ b/releasenotes/notes/agentless-crashtracking-fix-endpoint-e3e4b73aad92c90f.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + crashtracking: fix crash reports being sent to the wrong intake when ``DD_AGENTLESS_ENABLED`` + is set. The errors-intake upload was incorrectly reusing the same endpoint configured for the + telemetry crash-report path, sending it to the telemetry intake host instead of the dedicated + errors-intake host. \ No newline at end of file diff --git a/tests/crashtracker/test_crashtracker.py b/tests/crashtracker/test_crashtracker.py index 4ba215fcd65..bc8d211729f 100644 --- a/tests/crashtracker/test_crashtracker.py +++ b/tests/crashtracker/test_crashtracker.py @@ -1073,10 +1073,13 @@ def test_crashtracker_unhandled_exception(run_python_code_in_subprocess): @pytest.mark.subprocess(env={"DD_AGENTLESS_ENABLED": "true", "DD_API_KEY": "foobarkey"}) def test_crashtracker_uploads_to_the_intake_when_agentless(): - """Crash reports ride the telemetry intake, so agentless has to supply a key with the URL. + """Agentless must not pin crashtracking to a single, explicitly-computed endpoint. - libdatadog only resolves the direct intake path over the agent's telemetry proxy path when the - endpoint carries an API key *and* the receiver has direct submission enabled. + The crash-report (telemetry intake) and errors-intake uploads go to genuinely different + hosts. Forcing one endpoint on both would send the errors-intake upload to the wrong host, + so we pass no endpoint/api_key at all here - the receiver resolves both independently from + DD_API_KEY/DD_SITE/_DD_DIRECT_SUBMISSION_ENABLED, which must be forwarded to it explicitly + since its environment is not inherited (it's spawned via a raw execve). """ from unittest import mock @@ -1099,9 +1102,11 @@ def fake_receiver(args, env, *rest): ): crashtracking._get_args({}) - assert captured["endpoint"] == "https://instrumentation-telemetry-intake.datadoghq.com/" - assert captured["api_key"] == "foobarkey" + assert captured["endpoint"] is None + assert captured["api_key"] is None assert captured["receiver_env"]["_DD_DIRECT_SUBMISSION_ENABLED"] == "true" + assert captured["receiver_env"]["DD_API_KEY"] == "foobarkey" + assert captured["receiver_env"]["DD_SITE"] == "datadoghq.com" @pytest.mark.subprocess(env={"DD_API_KEY": "foobarkey"})