-
Notifications
You must be signed in to change notification settings - Fork 431
chore: add a registry for supported integrations #13215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
scripts/integration_registry/_update_integration_registry_versions.py
Outdated
Show resolved
Hide resolved
tests/contrib/integration_registry/test_external_dependencies.py
Outdated
Show resolved
Hide resolved
tests/contrib/integration_registry/test_external_dependencies.py
Outdated
Show resolved
Hide resolved
tests/contrib/integration_registry/test_external_dependencies.py
Outdated
Show resolved
Hide resolved
scripts/integration_registry/_update_integration_registry_versions.py
Outdated
Show resolved
Hide resolved
Bootstrap import analysisComparison of import times between this PR and base. SummaryThe average import time from this PR is: 233 ± 2 ms. The average import time from base is: 235 ± 2 ms. The import time difference between this PR and base is: -1.9 ± 0.1 ms. Import time breakdownThe following import paths have shrunk:
|
BenchmarksBenchmark execution time: 2025-04-24 21:32:37 Comparing candidate commit f174820 in PR branch Found 0 performance improvements and 5 performance regressions! Performance is the same for 491 metrics, 4 unstable metrics. scenario:iast_aspects-format_map_aspect
scenario:iast_aspects-modulo_aspect_for_bytes_bytearray
scenario:iast_aspects-replace_aspect
scenario:iast_aspects-swapcase_aspect
scenario:telemetryaddmetric-1-distribution-metric-1-times
|
tests/contrib/integration_registry/registry_update_helpers/integration_registry_manager.py
Outdated
Show resolved
Hide resolved
tests/contrib/integration_registry/registry_update_helpers/update_orchestrator.py
Outdated
Show resolved
Hide resolved
tests/contrib/integration_registry/registry_update_helpers/update_orchestrator.py
Outdated
Show resolved
Hide resolved
tests/contrib/integration_registry/registry_update_helpers/integration_update_orchestrator.py
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔥 LGTM - I left some nits but they shouldn't be blocking. (Though I think the README links should be fixed if possible).
# UNCOMMENT TO RUN THE UPDATER LOCALLY, and comment out the above few lines, requires updating riotfile.py | ||
# to include the following within the riot env: | ||
# - "filelock" | ||
# - "pyyaml" | ||
# from tests.contrib.integration_registry.registry_update_helpers.integration_registry_updater import ( | ||
# IntegrationRegistryUpdater | ||
# ) | ||
# updater = IntegrationRegistryUpdater() | ||
# updater_succeeded = updater.run(data_file_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is it possible to have an environment variable for running it locally, that applies these changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because uncommenting this requires the user to add pyyaml
and filelock
to the riot dependencies for the venv suite they are running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe call this out in the README if we can't make it an env var flag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few nits, otherwise LGTM 👍
elif ( | ||
package in DEPENDENCY_TO_INTEGRATION_MAPPING | ||
and DEPENDENCY_TO_INTEGRATION_MAPPING[package] == integration | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check if this is redundant with the previous check?
min_final = min_v if min_v and min_v != "N/A" else None | ||
max_final = max_v if max_v and max_v != "N/A" else None | ||
if min_final is None and max_final is None: | ||
return None | ||
return {"min": min_final or "N/A", "max": max_final or "N/A"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
min_final = min_v if min_v and min_v != "N/A" else None | |
max_final = max_v if max_v and max_v != "N/A" else None | |
if min_final is None and max_final is None: | |
return None | |
return {"min": min_final or "N/A", "max": max_final or "N/A"} | |
min_final = min_v if min_v and min_v != "N/A" else "N/A" | |
max_final = max_v if max_v and max_v != "N/A" else "N/A" | |
if min_final is "N/A" and max_final is "N/A": | |
return None | |
return {"min": min_final, "max": max_final} |
for entry in current_integrations: | ||
if not isinstance(entry, dict): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe refactor so it's less nested?
|
||
@pytest.fixture(scope="module") | ||
def riot_venv_names() -> set[str]: | ||
"""Recursively finds all Venv names defined in riotfile.py.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
"""Recursively finds all Venv names defined in riotfile.py.""" | |
"""Finds all Venv names defined in riotfile.py.""" |
- '@core' | ||
- '@contrib' | ||
- '@tracing' | ||
- '@integration_registry' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you added this above?
* Runs [`scripts/integration_registry/_update_integration_registry_versions.py`](../../../scripts/integration_registry/_update_integration_registry_versions.py) to update the registry | ||
* Formats the registry YAML for consistency | ||
|
||
* NOTE: Manual script update does not guarantee that newly added patched dependencies (such as for a new integration) will be added to the registry.yaml. A full test run is required for this process. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm slightly confused by this. So running the script manually would only take the existing registry and update its values, but won't recognize that the riotfile has been updated with new tests/integrations?
if entry.get("is_external_package") is True: | ||
integration_name = entry.get("integration_name", "UNKNOWN_ENTRY") | ||
dependency_names = entry.get("dependency_name", []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
if entry.get("is_external_package") is True: | |
integration_name = entry.get("integration_name", "UNKNOWN_ENTRY") | |
dependency_names = entry.get("dependency_name", []) | |
if not entry.get("is_external_package"): | |
continue | |
integration_name = entry.get("integration_name", "UNKNOWN_ENTRY") | |
dependency_names = entry.get("dependency_name", []) | |
... |
if not isinstance(dep_name, str) or not dep_name: | ||
errors.append( | ||
f"External integration '{integration_name}' has invalid item in dependency_name list: " | ||
f"{dep_name}" | ||
) | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this not guaranteed by the schema you're defining? 🤔
# UNCOMMENT TO RUN THE UPDATER LOCALLY, and comment out the above few lines, requires updating riotfile.py | ||
# to include the following within the riot env: | ||
# - "filelock" | ||
# - "pyyaml" | ||
# from tests.contrib.integration_registry.registry_update_helpers.integration_registry_updater import ( | ||
# IntegrationRegistryUpdater | ||
# ) | ||
# updater = IntegrationRegistryUpdater() | ||
# updater_succeeded = updater.run(data_file_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe call this out in the README if we can't make it an env var flag?
contrib directory. | ||
""" | ||
|
||
UNTESTED_INTEGRATIONS = ["asynctest", "dbapi", "dbapi_async", "integration_registry", "gunicorn"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Should we just have these in some kind of const.py
file so you only have to update one place?
Description
This PR introduces an integration registry, that contains information for all supported integrations.
registry.yml
located inddtrace/contrib/integration_registry/
False
for stdlib Python packages or abstract integrations such as dbapiaioredis
which is untested and planned to be deprecatedmin
andmax
versions for what is testedAlong with the
registry.yaml
, this PR makes quite a few changes to setup the necessary information to connect Riot testing venvs, the dependencies in those tests, to the integration being tested.riotfile.py
updates:integration_name:sub-test
django_hosts
->django:django_hosts
psycopg2
(our integration name is psycopg) ->psycopg:psycopg2
graphene
->graphql:graphene
ddtrace/contrib/integration_registry/mappings.py
scripts/freshvenvs.py
updates.riot/requirements/*.txt
to the respective test environment name. This change ensures that dependency versions collected are actually correlated with the test suite venv being tested (as opposed totest suite X
relying ondependency Y==0.1
, whiletest suite Y
testsdependencyY>1.0
, previously the tested version ofY
would be recorded as0.1
, now its>1.0
.ddtrace/integration_registry/mappings.py
, instead of storing the mapping in this script fileAutomatic updates of integration
registry.yml
for contrib testingtests/contrib/*
, we patch thegetattr
function and listen forgetattr
calls where_datadog_patch
is being called on an object. This call is used in most of our contribs, and allows us access to the modules being patched during tests. If this attribute was used in thegetattr
call, we store the object it was called on, as well as the callstack, for later processing.registry.yaml
with data collected from the patched object and traceback.registry.yaml
update process during testing:IntegrationRegistryManager
class, which later processes the objects, checks if they were valid contribs, gets the dependency name of the patched top level module, ie:module kafka -> (using importlib.metadata) -> we get package names producing this module -> ie:
confluent-kafka``.- After
IntegrationRegistryManager
runs, we run theIntegrationRegistryUpdater
class in a separate virtual environment, since the class relies onriot
andpyyaml
, both of which are not available in riot virtual environments, so we make our own.-
IntegrationRegistryUpdater
:IntegrationRegistryManager
, which includes tested integration, patched dependency name, patched dependency versionregistry.yml
, and decides whether to update the file, depending on if the dependency / integration combo is already present in the file, or if the tested version is outside the current files version range for the dependency, ifTrue
, runsscripts/integration_registry/update_and_format_registry.py
.New Scripting:
scripts/integration_registry/_format_integration_registry.py
: Formats the integration registry with spaces between integration registries, since no python yaml packages offer this functionality. Done for readabilityscripts/integration_registry/_update_integration_registry_versions.py
: Updates the registry with new data fromsupported_versions_output.json
, which is generated from thescripts/freshvenvs.py
script.scripts/integration_registry/update_and_format_registry.py
: Runsscripts/freshvenvs.py
, and the former two scripts to update all of our test information and resave the new registry. This is the main script to call.Other updates:
registry.yml
was updated during CI, the new check fails the CI job, similar to riot lockfile check, and tells the user to update theregistry.yml
with the new information.Several tests have been added to ensure that the
registry.yaml
stays updated when new contribs are added:Registry Tests
ddtrace/contrib/internal
has an associated registry entryregistry.yaml
conforms to strict schemaexternal
dependency has corresponding tested version information inregistry.yaml
external
dependency exists on PypiRiotfile Tests
ddtrace/contrib/internal
has a corresponding riot venv name associated with ittests/contrib/
uses a venv name that is a directory withinddtrace/contrib/internal
, if the venv name is a subtest in the form ofintegration[sub-test]
, tests that theintegration
is located withinddtrace/contrib/internal
.Checklist
Reviewer Checklist