From f5ceb4a65b469b5ea5c9b5ad1a51ebd200a79a44 Mon Sep 17 00:00:00 2001 From: Andrew Zhang Date: Wed, 19 Jul 2023 14:52:06 -0400 Subject: [PATCH] Add logic for integration package files (#14544) * Add logic for integration package files * Change to yield * update * Update test_core.py --------- Co-authored-by: Ofek Lev --- .ddev/config.toml | 3 ++ ddev/src/ddev/cli/validate/openmetrics.py | 58 +++++++-------------- ddev/src/ddev/integration/core.py | 11 +++- ddev/tests/cli/validate/test_openmetrics.py | 23 ++++---- ddev/tests/integration/test_core.py | 23 ++++++++ 5 files changed, 67 insertions(+), 51 deletions(-) diff --git a/.ddev/config.toml b/.ddev/config.toml index b22b610aa3a88..ef54a15d0f951 100644 --- a/.ddev/config.toml +++ b/.ddev/config.toml @@ -128,3 +128,6 @@ snowflake-connector-python = 'https://github.com/snowflakedb/snowflake-connector supervisor = 'https://github.com/Supervisor/supervisor' tuf = 'https://github.com/theupdateframework/python-tuf' typing = 'https://github.com/python/typing' + +[overrides.validate.openmetrics] +exclude = ["openmetrics"] diff --git a/ddev/src/ddev/cli/validate/openmetrics.py b/ddev/src/ddev/cli/validate/openmetrics.py index 2d76957a06f12..3cde77816678d 100644 --- a/ddev/src/ddev/cli/validate/openmetrics.py +++ b/ddev/src/ddev/cli/validate/openmetrics.py @@ -3,7 +3,6 @@ # Licensed under a 3-clause BSD style license (see LICENSE) from __future__ import annotations -import os from typing import TYPE_CHECKING import click @@ -11,17 +10,8 @@ if TYPE_CHECKING: from ddev.cli.application import Application -SKIPPED_INTEGRATIONS = [ - "OpenMetrics", - "Datadog Checks Base", - "Datadog Checks Dev", -] - def _validate_openmetrics_integrations(contents, integration, package_file, validation_tracker): - if integration.display_name in SKIPPED_INTEGRATIONS: - return False - # Note: can't include the closing parenthesis since some may include ConfigMixin if '(OpenMetricsBaseCheckV2' in contents or '(OpenMetricsBaseCheck' in contents: if 'DEFAULT_METRIC_LIMIT = 0' not in contents: @@ -33,27 +23,16 @@ def _validate_openmetrics_integrations(contents, integration, package_file, vali return False -def _get_python_files(directory): - python_files = [] - for root, _, files in os.walk(directory): - for file in files: - if file.endswith(".py"): - python_files.append(os.path.join(root, file)) - return python_files - - @click.command(short_help='Validate OpenMetrics') @click.argument('integrations', nargs=-1) -@click.pass_context -def openmetrics(ctx: click.Context, integrations: tuple[str, ...]): +@click.pass_obj +def openmetrics(app: Application, integrations: tuple[str, ...]): """Validate OpenMetrics metric limit. If `check` is specified, only the check will be validated, if check value is 'changed' will only apply to changed checks, an 'all' or empty `check` value will validate nothing. """ - - app: Application = ctx.obj - validation_tracker = app.create_validation_tracker('OpenMetrics Metric limit') + validation_tracker = app.create_validation_tracker('OpenMetrics metric limit') app.display_waiting("Validating default metric limit for OpenMetrics integrations ...") if app.repo.name not in ('core', 'extras'): @@ -63,26 +42,27 @@ def openmetrics(ctx: click.Context, integrations: tuple[str, ...]): ) app.abort() + excluded = set(app.repo.config.get('/overrides/validate/openmetrics/exclude', [])) for integration in app.repo.integrations.iter_packages(integrations): - pass_validation = False - python_files = _get_python_files(integration.package_directory) + if integration.name in excluded or not integration.is_integration: + continue - for file in python_files: - try: - f = open(file) - contents = f.read() - except Exception: - app.display_info(f"Could not open or read file {file}, skipping") + for f in integration.package_files(): + contents = f.read_text() + + # Note: can't include the closing parenthesis since some may include ConfigMixin + if not ('(OpenMetricsBaseCheckV2' in contents or '(OpenMetricsBaseCheck' in contents): + continue + + if 'DEFAULT_METRIC_LIMIT = 0' in contents: + validation_tracker.success() else: - pass_validation = pass_validation or _validate_openmetrics_integrations( - contents, integration, file, validation_tracker + validation_tracker.error( + (integration.display_name, str(f.relative_to(app.repo.path))), + message="`DEFAULT_METRIC_LIMIT = 0` is missing", ) - if pass_validation: - validation_tracker.success() + validation_tracker.display() if validation_tracker.errors: - validation_tracker.display() app.abort() - - validation_tracker.display() diff --git a/ddev/src/ddev/integration/core.py b/ddev/src/ddev/integration/core.py index 152b80fa22280..64e2b6e9b9650 100644 --- a/ddev/src/ddev/integration/core.py +++ b/ddev/src/ddev/integration/core.py @@ -3,15 +3,16 @@ # Licensed under a 3-clause BSD style license (see LICENSE) from __future__ import annotations +import os from functools import cached_property -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Iterator from ddev.repo.constants import NOT_SHIPPABLE +from ddev.utils.fs import Path if TYPE_CHECKING: from ddev.integration.manifest import Manifest from ddev.repo.config import RepositoryConfig - from ddev.utils.fs import Path class Integration: @@ -59,6 +60,12 @@ def package_directory(self) -> Path: return self.path / 'datadog_checks' / directory + def package_files(self) -> Iterator[Path]: + for root, _, files in os.walk(self.package_directory): + for f in files: + if f.endswith('.py'): + yield Path(root, f) + @property def release_tag_pattern(self) -> str: version_part = r'\d+\.\d+\.\d+' diff --git a/ddev/tests/cli/validate/test_openmetrics.py b/ddev/tests/cli/validate/test_openmetrics.py index 4ff6ea2cc278a..e53feb66e1b01 100644 --- a/ddev/tests/cli/validate/test_openmetrics.py +++ b/ddev/tests/cli/validate/test_openmetrics.py @@ -1,29 +1,30 @@ import pytest +@pytest.mark.usefixtures('repository') @pytest.mark.parametrize( - "check_name", + "check_name, classes", [ - pytest.param("aerospike", id="Aerospike check.py OpenMetricsV2"), - pytest.param("amazon_msk", id="Amazon MSK amazon_msk.py OpenMetricsV1 and V2"), + pytest.param("aerospike", 1, id="Aerospike check.py OpenMetricsV2"), + pytest.param("amazon_msk", 2, id="Amazon MSK amazon_msk.py OpenMetricsV1 and V2"), ], ) -def test_openmetrics_pass_single_parameter(ddev, repository, check_name, helpers, network_replay): +def test_openmetrics_pass_single_parameter(ddev, helpers, check_name, classes): result = ddev("validate", "openmetrics", check_name) assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( - """ + f""" Validating default metric limit for OpenMetrics integrations ... - OpenMetrics Metric limit + OpenMetrics metric limit - Passed: 1 + Passed: {classes} """ ) -def test_openmetrics_fail_single_parameter(ddev, helpers, repository, network_replay): +def test_openmetrics_fail_single_parameter(ddev, helpers, repository): missing_metric_limit = ''' class ArangodbCheck(OpenMetricsBaseCheckV2, ConfigMixin): __NAMESPACE__ = 'arangodb' @@ -44,7 +45,8 @@ def __init__(self, name, init_config, instances): assert "Errors: 1" in helpers.remove_trailing_spaces(result.output) -def test_openmetrics_skip_openmetrics(ddev, helpers, repository, network_replay): +@pytest.mark.usefixtures('repository') +def test_openmetrics_skip_openmetrics(ddev, helpers): result = ddev("validate", "openmetrics", "openmetrics") assert result.exit_code == 0, result.output @@ -53,6 +55,7 @@ def test_openmetrics_skip_openmetrics(ddev, helpers, repository, network_replay) assert "Errors" not in helpers.remove_trailing_spaces(result.output) +@pytest.mark.usefixtures('repository') @pytest.mark.parametrize( "repo, expected_message", [ @@ -64,7 +67,7 @@ def test_openmetrics_skip_openmetrics(ddev, helpers, repository, network_replay) ), ], ) -def test_openmetrics_validate_repo(repo, repository, expected_message, ddev, helpers, config_file): +def test_openmetrics_validate_repo(repo, expected_message, ddev, helpers, config_file): config_file.model.repo = repo config_file.save() diff --git a/ddev/tests/integration/test_core.py b/ddev/tests/integration/test_core.py index a1d07018900c1..3a5404da7a449 100644 --- a/ddev/tests/integration/test_core.py +++ b/ddev/tests/integration/test_core.py @@ -1,7 +1,10 @@ # (C) Datadog, Inc. 2022-present # All rights reserved # Licensed under a 3-clause BSD style license (see LICENSE) +import os + from ddev.repo.core import Repository +from ddev.utils.fs import Path def test_attributes(local_repo, valid_integration): @@ -189,6 +192,26 @@ def test_normalization(self, local_repo): assert integration.package_directory == local_repo / integration.name / 'datadog_checks' / 'go_metro' +class TestPackageFiles: + def test_base_package_file(self, local_repo): + repo = Repository(local_repo.name, str(local_repo)) + integration = repo.integrations.get('datadog_checks_base') + + expected_files = [] + for root, _, files in os.walk(integration.package_directory): + for f in files: + if f.endswith(".py"): + expected_files.append(Path(root, f)) + + assert list(integration.package_files()) == expected_files + + def test_tile_only_package_file(self, local_repo): + repo = Repository(local_repo.name, str(local_repo)) + integration = repo.integrations.get('agent_metrics') + + assert not list(integration.package_files()) + + class TestReleaseTagPattern: def test_shipped(self, local_repo): repo = Repository(local_repo.name, str(local_repo))