diff --git a/datadog_checks_base/changelog.d/23644.fixed b/datadog_checks_base/changelog.d/23644.fixed new file mode 100644 index 0000000000000..c420621b3d3a1 --- /dev/null +++ b/datadog_checks_base/changelog.d/23644.fixed @@ -0,0 +1 @@ +Add `get_agent_embedded_path` helper to resolve paths under the agent's embedded directory regardless of install location. \ No newline at end of file diff --git a/datadog_checks_base/datadog_checks/base/utils/agent/common.py b/datadog_checks_base/datadog_checks/base/utils/agent/common.py index 18a15fb62727d..cc6ce6d0c79d3 100644 --- a/datadog_checks_base/datadog_checks/base/utils/agent/common.py +++ b/datadog_checks_base/datadog_checks/base/utils/agent/common.py @@ -1,5 +1,25 @@ # (C) Datadog, Inc. 2019-present # All rights reserved # Licensed under a 3-clause BSD style license (see LICENSE) +import os +import sys + +try: + import datadog_agent +except ImportError: + from datadog_checks.base.stubs import datadog_agent + METRIC_NAMESPACE_METRICS = 'datadog.agent.metrics' METRIC_NAMESPACE_PROFILE = 'datadog.agent.profile' + + +def get_agent_embedded_path(*parts: str) -> str | None: + """Resolve a path under the agent's `embedded` directory, or ``None`` if unavailable.""" + if os.name == 'nt': + install_path = sys.executable.split('embedded')[0].rstrip(os.sep) + return os.path.join(install_path, 'embedded3', *parts) + run_path = datadog_agent.get_config('run_path') + if not run_path: + return None + install_path = run_path[:-4] if run_path.endswith('/run') else run_path + return os.path.join(install_path, 'embedded', *parts) diff --git a/datadog_checks_base/tests/base/utils/test_agent_common.py b/datadog_checks_base/tests/base/utils/test_agent_common.py new file mode 100644 index 0000000000000..7bdbfcf42f168 --- /dev/null +++ b/datadog_checks_base/tests/base/utils/test_agent_common.py @@ -0,0 +1,60 @@ +# (C) Datadog, Inc. 2026-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +import os + +import mock +import pytest + +from datadog_checks.base.utils.agent.common import get_agent_embedded_path + + +@pytest.mark.parametrize( + 'run_path, parts, expected_install', + [ + pytest.param('/opt/datadog-agent/run', ('sbin', 'gstatus'), '/opt/datadog-agent', id='standard_install'), + pytest.param( + '/opt/datadog-packages/datadog-agent/7.79.0/run', + ('sbin', 'gstatus'), + '/opt/datadog-packages/datadog-agent/7.79.0', + id='remote_management_install', + ), + pytest.param( + '/custom/agent/dir', ('ssl', 'certs', 'cacert.pem'), '/custom/agent/dir', id='run_path_without_trailing_run' + ), + pytest.param('/opt/datadog-agent/run', (), '/opt/datadog-agent', id='no_parts'), + ], +) +def test_get_agent_embedded_path_posix(run_path, parts, expected_install): + with ( + mock.patch('datadog_checks.base.utils.agent.common.os.name', 'posix'), + mock.patch( + 'datadog_checks.base.utils.agent.common.datadog_agent.get_config', + return_value=run_path, + ), + ): + assert get_agent_embedded_path(*parts) == os.path.join(expected_install, 'embedded', *parts) + + +def test_get_agent_embedded_path_missing_run_path_returns_none(): + with ( + mock.patch('datadog_checks.base.utils.agent.common.os.name', 'posix'), + mock.patch( + 'datadog_checks.base.utils.agent.common.datadog_agent.get_config', + return_value='', + ), + ): + assert get_agent_embedded_path('sbin', 'gstatus') is None + + +def test_get_agent_embedded_path_windows_uses_sys_executable(): + """On Windows, derive from sys.executable and use the embedded3 directory.""" + install_dir = r'C:\Program Files\Datadog\Datadog Agent' + sys_executable = os.path.join(install_dir, 'embedded3', 'python.exe') + with ( + mock.patch('datadog_checks.base.utils.agent.common.os.name', 'nt'), + mock.patch('datadog_checks.base.utils.agent.common.sys.executable', sys_executable), + ): + assert get_agent_embedded_path('ssl', 'certs', 'cacert.pem') == os.path.join( + install_dir, 'embedded3', 'ssl', 'certs', 'cacert.pem' + )