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
3 changes: 3 additions & 0 deletions .ddev/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ teamcity = "TeamCity"
win32_event_log = "Windows Event Log"
krakend = "KrakenD"
lustre = "Lustre"
kueue = "Kueue"
prefect = "Prefect"
n8n = "n8n"
hpe_aruba_edgeconnect = "HPE Aruba EdgeConnect"
Expand All @@ -51,6 +52,7 @@ dell_powerflex = "Dell Powerflex"
[overrides.metrics-prefix]
krakend = "krakend.api."
lustre = "lustre."
kueue = "kueue."
prefect = "prefect.server."
n8n = "n8n."
control_m = "control_m."
Expand Down Expand Up @@ -270,6 +272,7 @@ __pycache__ = false
[overrides.manifest.platforms]
krakend = ["linux", "windows", "mac_os"]
lustre = ["linux", "windows", "mac_os"]
kueue = ["linux", "windows", "mac_os"]
prefect = ["linux", "windows", "mac_os"]
n8n = ["linux", "windows", "mac_os"]
control_m = ["linux", "windows", "mac_os"]
Expand Down
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,10 @@ plaid/assets/logs/ @DataDog/saa
/gpu/*.md @DataDog/ebpf-platform @DataDog/documentation
/gpu/manifest.json @DataDog/ebpf-platform @DataDog/agent-integrations @DataDog/documentation

/kueue/ @DataDog/gpu-monitoring-agent
/kueue/*.md @DataDog/gpu-monitoring-agent @DataDog/documentation
/kueue/manifest.json @DataDog/gpu-monitoring-agent @DataDog/agent-integrations @DataDog/documentation

/linux_audit_logs/ @DataDog/agent-integrations
/linux_audit_logs/*.md @DataDog/agent-integrations @DataDog/documentation
/linux_audit_logs/manifest.json @DataDog/agent-integrations @DataDog/documentation
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/config/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,10 @@ integration/kubevirt_handler:
- changed-files:
- any-glob-to-any-file:
- kubevirt_handler/**/*
integration/kueue:
- changed-files:
- any-glob-to-any-file:
- kueue/**/*
integration/kuma:
- changed-files:
- any-glob-to-any-file:
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/test-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2338,6 +2338,26 @@ jobs:
minimum-base-package: ${{ inputs.minimum-base-package }}
pytest-args: ${{ inputs.pytest-args }}
secrets: inherit
j3c620e6:
uses: ./.github/workflows/test-target.yml
with:
job-name: Kueue
target: kueue
platform: linux
runner: '["ubuntu-22.04"]'
repo: "${{ inputs.repo }}"
context: ${{ inputs.context }}
python-version: "${{ inputs.python-version }}"
latest: ${{ inputs.latest }}
agent-image: "${{ inputs.agent-image }}"
agent-image-py2: "${{ inputs.agent-image-py2 }}"
agent-image-windows: "${{ inputs.agent-image-windows }}"
agent-image-windows-py2: "${{ inputs.agent-image-windows-py2 }}"
test-py2: ${{ inputs.test-py2 }}
test-py3: ${{ inputs.test-py3 }}
minimum-base-package: ${{ inputs.minimum-base-package }}
pytest-args: ${{ inputs.pytest-args }}
secrets: inherit
j739f9be:
uses: ./.github/workflows/test-target.yml
with:
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
| --- | --- |
| CI/CD | [![CI - Test][1]][2] |
| Docs | [![Docs - Release][19]][20] |
| Meta | [![Hatch project][26]][27] [![Linting - Ruff][24]][25] [![Code style - black][21]][22] [![Typing - Mypy][28]][29] [![License - BSD-3-Clause][30]][31] |
| Meta | [![Hatch project][26]][27] [![Linting - Ruff][24]][25] [![Typing - Mypy][28]][29] [![License - BSD-3-Clause][30]][31] |

This repository contains open source integrations that Datadog officially develops and supports.
To add a new integration, please see the [Integrations Extras][5] repository and the
Expand Down Expand Up @@ -43,10 +43,8 @@ For more information on integrations, please reference our [documentation][11] a
[16]: https://github.com/DataDog/integrations-core/blob/ea2dfbf1e8859333af4c8db50553eb72a3b466f9/requirements-agent-release.txt
[19]: https://github.com/DataDog/integrations-core/workflows/docs/badge.svg
[20]: https://github.com/DataDog/integrations-core/actions?workflow=docs
[21]: https://img.shields.io/badge/code%20style-black-000000.svg
[22]: https://github.com/ambv/black
[24]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v0.json
[25]: https://github.com/charliermarsh/ruff
[24]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
[25]: https://github.com/astral-sh/ruff
[26]: https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg
[27]: https://github.com/pypa/hatch
[28]: https://img.shields.io/badge/typing-Mypy-blue.svg
Expand Down
3 changes: 3 additions & 0 deletions code-coverage.datadog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ services:
- id: kubevirt_handler
paths:
- kubevirt_handler/datadog_checks/kubevirt_handler/
- id: kueue
paths:
- kueue/datadog_checks/kueue/
- id: kuma
paths:
- kuma/datadog_checks/kuma/
Expand Down
1 change: 1 addition & 0 deletions datadog_checks_dev/changelog.d/23588.changed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Stop declaring `black` as a direct dependency. The `apply_black` calls used to format auto-generated config-model files now go through `ruff format`, using the repo's centralized `[tool.ruff]` configuration.
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ def models(ctx, check, sync, verbose):
license_header_lines = get_license_header().splitlines(True) + ['\n', '\n']
documentation_header_lines = get_config_models_documentation().splitlines(True) + ['\n']

code_formatter = ModelConsumer.create_code_formatter()

if is_core_check:
checks = checks.difference(INTEGRATIONS_WITHOUT_MODELS)

Expand Down Expand Up @@ -135,7 +133,7 @@ def models(ctx, check, sync, verbose):
if not sync and not dir_exists(models_location) and not is_core_check:
continue

model_consumer = ModelConsumer(spec.data, code_formatter)
model_consumer = ModelConsumer(spec.data)

# So formatters see config files
with chdir(root):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# (C) Datadog, Inc. 2026-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
import shlex
import subprocess
import sys
from pathlib import Path

from datadog_checks.dev.tooling.constants import get_root


def format_with_ruff(source: str) -> str:
"""Format Python source via ``ruff format -`` (stdin/stdout).

Replaces the line-wrapping role previously played by black on auto-generated
config_models files. Uses the repo's centralized ruff configuration so the
output matches the rest of the codebase. Invokes ruff through the active
interpreter (``python -m ruff``) so the package installed alongside
``datadog_checks_dev[cli]`` is always picked up, regardless of PATH.
"""
args = [sys.executable, '-m', 'ruff', 'format', '--quiet', '--stdin-filename=model.py']
config_path = _resolve_ruff_config()
if config_path is not None:
args.extend(['--config', str(config_path)])
else:
args.extend(['--isolated', '--config', "format.quote-style='preserve'", '--line-length=120'])
args.append('-')

try:
result = subprocess.run(
args,
input=source,
capture_output=True,
text=True,
check=True,
)
except subprocess.CalledProcessError as e:
# `python -m ruff` exits non-zero when the ruff package is missing,
# surfacing as ModuleNotFoundError on stderr. Promote that to a
# clearer install hint; otherwise propagate the underlying error
# with enough context to reproduce the failure manually.
stderr = e.stderr or ''
if "No module named 'ruff'" in stderr:
raise RuntimeError(
"Cannot format auto-generated config models: the `ruff` package is not installed in the active "
"interpreter. Reinstall `datadog_checks_dev[cli]` (or run `pip install ruff`) and retry."
) from e
details = [f'{shlex.join(args)} failed', f'stderr: {stderr.strip()}']
if e.stdout:
details.append(f'stdout: {e.stdout.strip()}')
raise RuntimeError(
'`ruff format` failed while formatting auto-generated config models. ' + '; '.join(details)
) from e
return result.stdout


def _resolve_ruff_config() -> Path | None:
"""Locate the repo pyproject.toml that holds the central ruff configuration.

Prefer the path reported by ``get_root`` (set by ddev commands). Fall back
to walking up from this module so unit tests, which never call ``set_root``,
still pick up the same configuration as model regeneration.
"""
root_str = get_root()
if root_str:
root = Path(root_str)
if root.is_dir():
candidate = root / 'pyproject.toml'
if _has_ruff_section(candidate):
return candidate

for parent in Path(__file__).resolve().parents:
candidate = parent / 'pyproject.toml'
if _has_ruff_section(candidate):
return candidate
return None


def _has_ruff_section(pyproject: Path) -> bool:
if not pyproject.is_file():
return False
try:
text = pyproject.read_text()
except OSError:
return False
return any(
stripped == '[tool.ruff]' or stripped.startswith('[tool.ruff.')
for stripped in (line.strip() for line in text.splitlines())
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
import warnings
from pathlib import Path
from typing import Dict, List, Tuple

import yaml
from datamodel_code_generator import DataModelType
from datamodel_code_generator.format import CodeFormatter, PythonVersion
from datamodel_code_generator.format import PythonVersion
from datamodel_code_generator.model import get_data_model_types
from datamodel_code_generator.parser import LiteralType
from datamodel_code_generator.parser.openapi import OpenAPIParser

from datadog_checks.dev.tooling.configuration.consumers.model.code_formatter import format_with_ruff
from datadog_checks.dev.tooling.configuration.consumers.model.model_file import build_model_file
from datadog_checks.dev.tooling.configuration.consumers.model.model_info import ModelInfo
from datadog_checks.dev.tooling.configuration.consumers.openapi_document import build_openapi_document
from datadog_checks.dev.tooling.constants import get_root

PYTHON_VERSION = PythonVersion.PY_39

Expand All @@ -32,9 +31,8 @@


class ModelConsumer:
def __init__(self, spec: dict, code_formatter: CodeFormatter = None):
def __init__(self, spec: dict):
self.spec = spec
self.code_formatter = code_formatter or self.create_code_formatter()

def render(self) -> Dict[str, Dict[str, str]]:
"""
Expand Down Expand Up @@ -137,7 +135,6 @@ def _process_section(self, section) -> (List[Tuple[str, str]], dict, ModelInfo):
model_id,
section_name,
model_info,
self.code_formatter,
)
# instance.py or shared.py
model_files[model_file_name] = (model_file_contents, errors)
Expand Down Expand Up @@ -207,11 +204,6 @@ def _merge_instances(self, section: dict, errors: List[str]) -> dict:

return new_section

@staticmethod
def create_code_formatter():
path = Path(get_root())
return CodeFormatter(PYTHON_VERSION, settings_path=path if path.is_dir() else None)

def _build_deprecation_file(self, deprecation_data):
file_needs_formatting = False
deprecations_file_lines = []
Expand All @@ -226,7 +218,7 @@ def _build_deprecation_file(self, deprecation_data):
deprecations_file_lines.append('')
deprecations_file_contents = '\n'.join(deprecations_file_lines)
if file_needs_formatting:
deprecations_file_contents = self.code_formatter.apply_black(deprecations_file_contents)
deprecations_file_contents = format_with_ruff(deprecations_file_contents)
return deprecations_file_contents

@staticmethod
Expand Down Expand Up @@ -255,5 +247,5 @@ def _build_defaults_file(self, model_info: ModelInfo):
model_info.defaults_file_lines.append('')
defaults_file_contents = '\n'.join(model_info.defaults_file_lines)
if model_info.defaults_file_needs_value_normalization:
defaults_file_contents = self.code_formatter.apply_black(defaults_file_contents)
defaults_file_contents = format_with_ruff(defaults_file_contents)
return defaults_file_contents
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# (C) Datadog, Inc. 2021-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from datamodel_code_generator.format import CodeFormatter

from datadog_checks.dev.tooling.configuration.consumers.model.code_formatter import format_with_ruff
from datadog_checks.dev.tooling.configuration.consumers.model.model_info import ModelInfo


Expand All @@ -11,14 +10,12 @@ def build_model_file(
model_id: str,
section_name: str,
model_info: ModelInfo,
code_formatter: CodeFormatter,
):
"""
:param parsed_document: OpenApi parsed document
:param model_id: instance or shared
:param section_name: init or instances
:param model_info: Information to build the model file
:param code_formatter:
"""
# Whether or not there are options with default values
options_with_defaults = len(model_info.defaults_file_lines) > 0
Expand Down Expand Up @@ -54,7 +51,7 @@ def build_model_file(
model_file_lines.append('')
model_file_contents = '\n'.join(model_file_lines)
if any(len(line) > 120 for line in model_file_lines):
model_file_contents = code_formatter.apply_black(model_file_contents)
model_file_contents = format_with_ruff(model_file_contents)
return model_file_contents


Expand Down Expand Up @@ -109,27 +106,44 @@ def _add_imports(model_file_lines, need_defaults, need_deprecations):


def _fix_types(model_file_lines):
for i, line in enumerate(model_file_lines):
line = model_file_lines[i] = line.replace('dict[', 'MappingProxyType[')
if 'list[' not in line:
continue

buffer = bytearray()
containers = []

for char in line:
if char == '[':
if buffer[-4:] == b'list':
containers.append(True)
buffer[-4:] = b'tuple'
else:
containers.append(False)
elif char == ']' and containers.pop():
buffer.extend(b', ...')

buffer.append(ord(char))

model_file_lines[i] = buffer.decode('utf-8')
# Operate on the joined document (as UTF-8 bytes) so the bracket-tracking
# pass below works even when the upstream parser pre-wraps `list[...]`
# across multiple lines. Iterating bytes keeps the algorithm safe for
# non-ASCII content (descriptions, examples) since `[`, `]`, and `list`
# are all single-byte ASCII while UTF-8 continuation bytes never collide
# with them.
content = '\n'.join(model_file_lines).replace('dict[', 'MappingProxyType[')
if 'list[' not in content:
model_file_lines[:] = content.split('\n')
return

encoded = content.encode('utf-8')
buffer = bytearray()
containers = []
open_bracket = ord(b'[')
close_bracket = ord(b']')
whitespace = (ord(b' '), ord(b'\t'), ord(b'\n'))

for byte in encoded:
if byte == open_bracket:
if buffer[-4:] == b'list':
containers.append(True)
buffer[-4:] = b'tuple'
else:
containers.append(False)
elif byte == close_bracket and containers and containers.pop():
# Insert `, ...` after the last non-whitespace byte already in the
# buffer so the sentinel sits on the same line as the previous
# content (`tuple[X], ...` style) even when the parser wrapped the
# closing `]` onto its own line.
insert_at = len(buffer)
while insert_at > 0 and buffer[insert_at - 1] in whitespace:
insert_at -= 1
buffer[insert_at:insert_at] = b', ...'

buffer.append(byte)

model_file_lines[:] = buffer.decode('utf-8').split('\n')


def _add_secure_fields_constant(model_file_lines, require_trusted_providers):
Expand Down
Loading
Loading