Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ updates:
interval: "weekly"
day: "monday"
time: "06:00"
cooldown:
default-days: 7
# Group all actions updates together in the same PR
groups:
actions:
Expand All @@ -23,7 +25,8 @@ updates:
interval: "weekly"
day: "monday"
time: "06:00"
# Group all actions updates together in the same PR
cooldown:
default-days: 7
groups:
actions:
patterns:
Expand Down
1 change: 1 addition & 0 deletions ddev/changelog.d/22838.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed `ddev size` ignore filtering to correctly interpret gitignore entries, ensuring excluded files are omitted from size calculations.
35 changes: 25 additions & 10 deletions ddev/src/ddev/cli/size/utils/common_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Licensed under a 3-clause BSD style license (see LICENSE)
from __future__ import annotations

import fnmatch
import json
import os
import re
Expand Down Expand Up @@ -160,7 +161,7 @@ def is_valid_integration_file(
included_folder = "datadog_checks" + os.sep

if git_ignore is None:
git_ignore = get_gitignore_files(repo_path)
git_ignore = get_gitignore_files(Path(repo_path))
# It is not an integration
if path.startswith("."):
return False
Expand All @@ -171,20 +172,34 @@ def is_valid_integration_file(
elif any(ignore in path for ignore in ignored_files):
return False
# This file is contained in .gitignore
elif any(ignore in path for ignore in git_ignore):
elif _matches_gitignore(path, git_ignore):
return False
else:
return True


def get_gitignore_files(repo_path: str | Path) -> list[str]:
gitignore_path = os.path.join(repo_path, ".gitignore")
with open(gitignore_path, "r", encoding="utf-8") as file:
gitignore_content = file.read()
ignored_patterns = [
line.strip() for line in gitignore_content.splitlines() if line.strip() and not line.startswith("#")
]
return ignored_patterns
def _matches_gitignore(path: str, patterns: list[str]) -> bool:
parts = path.replace(os.sep, "/").split("/")
for pattern in patterns:
norm = pattern.rstrip("/")
if fnmatch.fnmatch(path, norm):
return True
if fnmatch.fnmatch(os.path.basename(path), norm):
return True
if any(fnmatch.fnmatch(part, norm) for part in parts):
return True
return False


def get_gitignore_files(repo_path: Path) -> list[str]:
gitignore_path = repo_path / ".gitignore"
if not gitignore_path.is_file():
return []

with gitignore_path.open(mode="r", encoding="utf-8") as f:
lines = [line.strip() for line in f.read().splitlines() if line.strip() and not line.startswith("#")]

return lines


def convert_to_human_readable_size(size_bytes: float) -> str:
Expand Down
34 changes: 26 additions & 8 deletions ddev/tests/size/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytest

from ddev.cli.size.utils.common_funcs import (
_matches_gitignore,
check_python_version,
compress,
convert_to_human_readable_size,
Expand Down Expand Up @@ -115,14 +116,34 @@ def test_convert_to_human_readable_size(size_bytes, expected_string):
pytest.param("__pycache__/file.py", False, id="pycache"),
pytest.param("datadog_checks_dev/example.py", False, id="checks_dev"),
pytest.param(".git/config", False, id="git"),
pytest.param("datadog_checks/module/cache.pyc", False, id="gitignore_glob_ext"),
pytest.param("datadog_checks/module/__pycache__/foo.py", False, id="gitignore_glob_dir"),
],
)
def test_is_valid_integration_file(file_path, expected):
repo_path = "fake_repo"
with patch("ddev.cli.size.utils.common_funcs.get_gitignore_files", return_value=set()):
gitignore_patterns = ["*.pyc", "__pycache__"]
with patch("ddev.cli.size.utils.common_funcs.get_gitignore_files", return_value=gitignore_patterns):
assert is_valid_integration_file(to_native_path(file_path), repo_path) is expected


@pytest.mark.parametrize(
"path, patterns, expected",
[
pytest.param("foo/bar/baz.pyc", ["*.pyc"], True, id="glob_extension_match"),
pytest.param("foo/bar/baz.py", ["*.pyc"], False, id="glob_extension_no_match"),
pytest.param("foo/__pycache__/module.py", ["__pycache__"], True, id="dir_segment_match"),
pytest.param("foo/bar/module.py", ["__pycache__"], False, id="dir_segment_no_match"),
pytest.param("foo/bar/notes.log", ["*.log"], True, id="glob_log_match"),
pytest.param("foo/bar/notes.txt", ["*.log"], False, id="glob_log_no_match"),
pytest.param("foo/bar/baz.py", ["*.pyc", "__pycache__", "*.log"], False, id="no_pattern_matches"),
pytest.param("foo/__pycache__/baz.pyc", ["*.pyc", "__pycache__"], True, id="multiple_patterns_first_matches"),
],
)
def test_matches_gitignore(path, patterns, expected):
assert _matches_gitignore(to_native_path(path), patterns) is expected


def test_get_dependencies_list():
file_content = "dependency1 @ https://example.com/dependency1/dependency1-1.1.1-.whl\ndependency2 @ https://example.com/dependency2/dependency2-1.1.1-.whl"
mock_open_obj = mock_open(read_data=file_content)
Expand Down Expand Up @@ -267,13 +288,10 @@ def test_check_version(py_version, expected):
assert check_python_version("fake_repo", "integration1", py_version) is expected


def test_get_gitignore_files():
mock_gitignore = f"__pycache__{os.sep}\n*.log\n" # Sample .gitignore file
repo_path = "fake_repo"
with patch("builtins.open", mock_open(read_data=mock_gitignore)):
with patch("ddev.cli.size.utils.common_funcs.os.path.exists", return_value=True):
ignored_patterns = get_gitignore_files(repo_path)
assert ignored_patterns == ["__pycache__" + os.sep, "*.log"]
def test_get_gitignore_files(tmp_path):
gitignore = tmp_path / ".gitignore"
gitignore.write_text(f"__pycache__{os.sep}\n*.log\n")
assert get_gitignore_files(tmp_path) == ["__pycache__" + os.sep, "*.log"]


def test_compress():
Expand Down
1 change: 1 addition & 0 deletions nutanix/changelog.d/22836.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix categories collection for clusters
5 changes: 0 additions & 5 deletions nutanix/datadog_checks/nutanix/activity_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,6 @@ def _process_audit(self, audit: dict) -> None:
if entity_name := entity.get("name"):
audit_tags.append(f"ntnx_affected_entity_name:{entity_name}")

# Add category tags from affected entity
audit_tags.extend(self.check.extract_category_tags(entity))

audit_tags.append("ntnx_type:audit")

self.check.event(
Expand Down Expand Up @@ -513,7 +510,6 @@ def _process_task(self, task: dict) -> None:
task_tags.append(f"ntnx_entity_type:{entity_type}")
if entity_name := entity.get("name"):
task_tags.append(f"ntnx_entity_name:{entity_name}")
task_tags.extend(self.check.extract_category_tags(entity))

# Enrich with rendered alert title when the entity is an alert
if entity_type == "monitoring:serviceability:alert":
Expand Down Expand Up @@ -567,7 +563,6 @@ def _add_source_entity_tags(self, tags: list[str], item: dict) -> None:
if entity_type := source_entity.get("type"):
if entity_name := source_entity.get("name"):
tags.append(f"ntnx_{entity_type}_name:{entity_name}")
tags.extend(self.check.extract_category_tags(source_entity))

def _add_cluster_name_tag(self, tags: list[str], cluster_id: str | None, fallback_name: str | None = None) -> None:
"""Add cluster name tag from ID lookup, with optional fallback."""
Expand Down
37 changes: 27 additions & 10 deletions nutanix/datadog_checks/nutanix/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,33 @@ def extract_category_tags(self, entity: dict) -> list[str]:
categories = entity.get("categories")
if categories:
for c in categories:
category_id = c.get("extId")
if category_id and category_id in self.categories:
category = self.categories[category_id]
key = category.get("key")
value = category.get("value")
if key and value:
if self.prefix_category_tags:
tags.append(f"ntnx_{key}:{value}")
else:
tags.append(f"{key}:{value}")
if isinstance(c, dict):
category_id = c.get("extId")
elif isinstance(c, str):
category_id = c
else:
self.log.debug(
"Skipping unexpected category entry type=%s value=%r",
type(c),
c,
)
continue

if not category_id:
continue

category = self.categories.get(category_id)
if not isinstance(category, dict):
continue

key = category.get("key")
value = category.get("value")

if key and value:
if self.prefix_category_tags:
tags.append(f"ntnx_{key}:{value}")
else:
tags.append(f"{key}:{value}")
return tags

def check(self, _):
Expand Down
2 changes: 1 addition & 1 deletion nutanix/tests/fixtures/categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -915,4 +915,4 @@
"totalAvailableResults": 41
}
}
]
]
6 changes: 5 additions & 1 deletion nutanix/tests/fixtures/clusters.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
"currentClusterFaultTolerance": "CFT_1N_OR_1D",
"desiredClusterFaultTolerance": "CFT_1N_OR_1D"
},
"operationMode": "NORMAL",
"pulseStatus": {
"$reserved": {
"$fv": "v4.r1"
Expand Down Expand Up @@ -207,7 +208,10 @@
"isSegmentationEnabled": false
}
},
"upgradeStatus": "SUCCEEDED"
"upgradeStatus": "SUCCEEDED",
"categories": [
"66ae6983-5a95-3f0b-9e5d-b5b603b5f2c0"
]
},
{
"$reserved": {
Expand Down
9 changes: 9 additions & 0 deletions nutanix/tests/test_capacity_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def test_host_cpu_sockets(self, dd_run_check, aggregator, mock_instance, mock_ht
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand All @@ -140,6 +141,7 @@ def test_host_cpu_cores(self, dd_run_check, aggregator, mock_instance, mock_http
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand All @@ -161,6 +163,7 @@ def test_host_cpu_threads(self, dd_run_check, aggregator, mock_instance, mock_ht
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand All @@ -182,6 +185,7 @@ def test_host_memory_bytes(self, dd_run_check, aggregator, mock_instance, mock_h
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand All @@ -207,6 +211,7 @@ def test_cluster_cpu_total_cores(self, dd_run_check, aggregator, mock_instance,
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_cluster_name:datadog-nutanix-dev',
'nutanix',
'prism_central:10.0.0.197',
Expand All @@ -221,6 +226,7 @@ def test_cluster_cpu_total_threads(self, dd_run_check, aggregator, mock_instance
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_cluster_name:datadog-nutanix-dev',
'nutanix',
'prism_central:10.0.0.197',
Expand All @@ -235,6 +241,7 @@ def test_cluster_memory_total_bytes(self, dd_run_check, aggregator, mock_instanc
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_cluster_name:datadog-nutanix-dev',
'nutanix',
'prism_central:10.0.0.197',
Expand All @@ -249,6 +256,7 @@ def test_cluster_vcpus_allocated(self, dd_run_check, aggregator, mock_instance,
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_cluster_name:datadog-nutanix-dev',
'nutanix',
'prism_central:10.0.0.197',
Expand All @@ -263,6 +271,7 @@ def test_cluster_memory_allocated_bytes(self, dd_run_check, aggregator, mock_ins
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_cluster_name:datadog-nutanix-dev',
'nutanix',
'prism_central:10.0.0.197',
Expand Down
2 changes: 2 additions & 0 deletions nutanix/tests/test_clusters.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def test_cluster_metrics(dd_run_check, aggregator, mock_instance, mock_http_get)
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_cluster_name:datadog-nutanix-dev',
'nutanix',
'prism_central:10.0.0.197',
Expand All @@ -54,6 +55,7 @@ def test_cluster_stats_metrics(dd_run_check, aggregator, mock_instance, mock_htt
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_cluster_name:datadog-nutanix-dev',
'nutanix',
'prism_central:10.0.0.197',
Expand Down
4 changes: 4 additions & 0 deletions nutanix/tests/test_hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def test_host_metrics(dd_run_check, aggregator, mock_instance, mock_http_get):
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand All @@ -34,6 +35,7 @@ def test_host_stats_metrics(dd_run_check, aggregator, mock_instance, mock_http_g
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand All @@ -54,6 +56,7 @@ def test_host_status_metrics(dd_run_check, aggregator, mock_instance, mock_http_
dd_run_check(check)

expected_tags = [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand All @@ -76,6 +79,7 @@ def test_external_tags_for_host(dd_run_check, aggregator, mock_instance, mock_ht
'10-0-0-103-aws-us-east-1a',
{
'nutanix': [
'Team:agent-integrations',
'ntnx_type:host',
'ntnx_cluster_name:datadog-nutanix-dev',
'ntnx_host_name:10-0-0-103-aws-us-east-1a',
Expand Down
6 changes: 3 additions & 3 deletions nutanix/tests/test_resource_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
def test_default_collects_only_on_vms_and_user_category_tags(dd_run_check, aggregator, mock_instance, mock_http_get):
check = NutanixCheck('nutanix', {}, [mock_instance])
dd_run_check(check)
expected_tags = BASE_TAGS + ['ntnx_cluster_name:' + CLUSTER_NAME]
expected_tags = BASE_TAGS + ['Team:agent-integrations', 'ntnx_cluster_name:' + CLUSTER_NAME]
aggregator.assert_metric("nutanix.cluster.count", value=1, tags=expected_tags)
aggregator.assert_metric("nutanix.host.count", at_least=1)

Expand Down Expand Up @@ -49,7 +49,7 @@ def test_include_cluster_by_id(dd_run_check, aggregator, mock_instance, mock_htt
]
check = NutanixCheck('nutanix', {}, [mock_instance])
dd_run_check(check)
expected_tags = BASE_TAGS + ['ntnx_cluster_name:' + CLUSTER_NAME]
expected_tags = BASE_TAGS + ['Team:agent-integrations', 'ntnx_cluster_name:' + CLUSTER_NAME]
aggregator.assert_metric("nutanix.cluster.count", value=1, tags=expected_tags)


Expand Down Expand Up @@ -102,7 +102,7 @@ def test_multiple_include_patterns(dd_run_check, aggregator, mock_instance, mock
]
check = NutanixCheck('nutanix', {}, [mock_instance])
dd_run_check(check)
expected_tags = BASE_TAGS + ['ntnx_cluster_name:' + CLUSTER_NAME]
expected_tags = BASE_TAGS + ['Team:agent-integrations', 'ntnx_cluster_name:' + CLUSTER_NAME]
aggregator.assert_metric("nutanix.cluster.count", value=1, tags=expected_tags)


Expand Down
Loading