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
44 changes: 34 additions & 10 deletions .github/workflows/build-ddev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@ jobs:
fi
echo "base_tags=team:agent-integrations,service:ddev,context:$context" >> $GITHUB_OUTPUT

should-sign-and-upload:
name: Determine signing conditions
runs-on: ubuntu-latest
defaults:
run:
# Override workflow-level working-directory since this job does not checkout code
working-directory: .
outputs:
result: ${{ steps.check.outputs.result }}
steps:
- id: check
env:
EVENT_NAME: ${{ github.event_name }}
REF: ${{ github.ref }}
run: |
result=false
if [[ "$EVENT_NAME" == "schedule" || ( "$EVENT_NAME" == "push" && "$REF" == refs/tags/* ) ]]; then
result=true
fi
echo "result=$result" >> $GITHUB_OUTPUT

python-artifacts:
name: Build wheel and source distribution
runs-on: ubuntu-latest
Expand Down Expand Up @@ -315,9 +336,10 @@ jobs:

windows-packaging:
name: Build Windows installers
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
if: github.event_name == 'push' || github.event_name == 'schedule' || github.event.pull_request.head.repo.full_name == github.repository
needs:
- define-tags
- should-sign-and-upload
- binaries
runs-on: windows-2022
permissions:
Expand Down Expand Up @@ -429,6 +451,7 @@ jobs:
mv build/*/release/*/*.{exe,msi} installers

- name: Upload installers
if: needs.should-sign-and-upload.outputs.result == 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: installers-${{ runner.os }}
Expand All @@ -437,9 +460,10 @@ jobs:

macos-packaging:
name: Build macOS installer and sign/notarize artifacts
if: github.event_name == 'push'
if: github.event_name == 'push' || github.event_name == 'schedule' || github.event.pull_request.head.repo.full_name == github.repository
needs:
- define-tags
- should-sign-and-upload
- binaries
runs-on: macos-14-large
permissions:
Expand Down Expand Up @@ -483,6 +507,7 @@ jobs:
tar --strip-components=1 -xzf - -C /usr/local/bin "$ARCHIVE_NAME/rcodesign"

- name: Write credentials
if: needs.should-sign-and-upload.outputs.result == 'true'
env:
APPLE_DEVELOPER_ID_APPLICATION_CERTIFICATE: "${{ secrets.APPLE_DEVELOPER_ID_APPLICATION_CERTIFICATE }}"
APPLE_DEVELOPER_ID_APPLICATION_PRIVATE_KEY: "${{ secrets.APPLE_DEVELOPER_ID_APPLICATION_PRIVATE_KEY }}"
Expand Down Expand Up @@ -560,13 +585,12 @@ jobs:
- name: Extract staged standalone binaries
run: ${{ steps.script-extract.outputs.script }}

# Signing and notarization steps are skipped for Dependabot PRs (no access to Apple secrets)
- name: Sign standalone binaries
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
if: needs.should-sign-and-upload.outputs.result == 'true'
run: ${{ steps.script-sign.outputs.script }}

- name: Notarize standalone binaries
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
if: needs.should-sign-and-upload.outputs.result == 'true'
run: ${{ steps.script-notarize.outputs.script }}

- name: Archive standalone binaries
Expand Down Expand Up @@ -599,11 +623,11 @@ jobs:
run: ${{ steps.script-extract.outputs.script }}

- name: Sign managed binaries
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
if: needs.should-sign-and-upload.outputs.result == 'true'
run: ${{ steps.script-sign.outputs.script }}

- name: Notarize managed binaries
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
if: needs.should-sign-and-upload.outputs.result == 'true'
run: ${{ steps.script-notarize.outputs.script }}

# bin/<APP_NAME>-<VERSION>-<TARGET> -> targets/<TARGET>/<APP_NAME>
Expand Down Expand Up @@ -646,7 +670,7 @@ jobs:
echo "path=$pkg_file" >> "$GITHUB_OUTPUT"

- name: Sign PKG
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
if: needs.should-sign-and-upload.outputs.result == 'true'
run: >-
rcodesign sign -vv
--pem-source /tmp/certificate-installer.pem
Expand All @@ -655,15 +679,15 @@ jobs:
"signed/${{ steps.pkg.outputs.path }}"

- name: Notarize PKG
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
if: needs.should-sign-and-upload.outputs.result == 'true'
run: >-
rcodesign notary-submit
--api-key-path /tmp/app-store-connect.json
--staple
"signed/${{ steps.pkg.outputs.path }}"

- name: Upload installer
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
if: needs.should-sign-and-upload.outputs.result == 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: installers-${{ runner.os }}
Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/days-since-last-pin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Days Since Last Pin

on:
schedule:
- cron: "42 9 * * *"
workflow_dispatch:

jobs:
compute-days-since-last-pin:
runs-on: ubuntu-22.04
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- uses: DataDog/dd-octo-sts-action@acaa02eee7e3bb0839e4272dacb37b8f3b58ba80 # v1.0.3
id: octo-sts
with:
scope: DataDog/datadog-agent
policy: integrations-core.github.read-release-json.schedule

- name: Compute days since last pin
id: compute
env:
GITHUB_TOKEN: ${{ steps.octo-sts.outputs.token }}
run: python3 .github/workflows/scripts/days_since_last_pin.py

- name: Submit metric to Datadog
env:
DD_API_KEY: ${{ secrets.DD_API_KEY }}
DAYS: ${{ steps.compute.outputs.days }}
run: |
python3 - <<'EOF'
import json, os, time, urllib.request
days = int(os.environ["DAYS"])
payload = json.dumps({"series": [{"metric": "integrations_core.days_since_last_pin", "type": 3, "points": [{"timestamp": int(time.time()), "value": days}], "tags": ["team:agent-integrations"]}]}).encode()
req = urllib.request.Request("https://api.datadoghq.com/api/v2/series", data=payload, headers={"DD-API-KEY": os.environ["DD_API_KEY"], "Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(req) as resp:
print(f"Datadog API response: {json.loads(resp.read().decode())}")
EOF
86 changes: 86 additions & 0 deletions .github/workflows/scripts/days_since_last_pin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import json
import os
import urllib.request
from datetime import datetime, timedelta, timezone
from typing import Any

AGENT_REPO = "DataDog/datadog-agent"
RELEASE_JSON_URL = f"https://raw.githubusercontent.com/{AGENT_REPO}/main/release.json"
COMMITS_API_URL = f"https://api.github.com/repos/{AGENT_REPO}/commits"
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]


def fetch_json(url: str, headers: dict[str, str] | None = None) -> Any:
req = urllib.request.Request(url, headers=headers or {})
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode())


def get_integrations_core_version(sha: str) -> str:
raw_url = f"https://raw.githubusercontent.com/{AGENT_REPO}/{sha}/release.json"
req = urllib.request.Request(raw_url, headers={"Authorization": f"token {GITHUB_TOKEN}"})
with urllib.request.urlopen(req) as resp:
release = json.loads(resp.read().decode())
return release["dependencies"]["INTEGRATIONS_CORE_VERSION"]


# Step 1: get current pin
req = urllib.request.Request(RELEASE_JSON_URL, headers={"Authorization": f"token {GITHUB_TOKEN}"})
with urllib.request.urlopen(req) as resp:
release_data = json.loads(resp.read().decode())
current_pin = release_data["dependencies"]["INTEGRATIONS_CORE_VERSION"]
print(f"Current pin: {current_pin}")

# Steps 2 & 3: fetch commits to release.json from the last month, walk newest→oldest.
# release.json is updated for many reasons beyond the integrations-core pin, so we bound by
# time window rather than a fixed page count. If the pin has been unchanged for the full month,
# last_pin_commit will hold the oldest commit in the window (a conservative undercount, but
# still well above the 4-day alert threshold).
since = (datetime.now(timezone.utc) - timedelta(days=30)).strftime("%Y-%m-%dT%H:%M:%SZ")
last_pin_commit: dict | None = None
pin_changed = False
page = 1

while not pin_changed:
page_commits = fetch_json(
f"{COMMITS_API_URL}?path=release.json&per_page=100&since={since}&page={page}",
headers={"Authorization": f"token {GITHUB_TOKEN}", "Accept": "application/vnd.github+json"},
)
if not page_commits:
break
print(f"Page {page}: fetched {len(page_commits)} commits touching release.json")
if last_pin_commit is None:
last_pin_commit = page_commits[0] # most recent commit, used as fallback
for commit in page_commits:
sha = commit["sha"]
try:
pin_at_sha = get_integrations_core_version(sha)
except Exception as e:
print(f"Error: could not fetch release.json at {sha}: {e}")
raise # fail the job; don't submit a potentially wrong metric
if pin_at_sha == current_pin:
last_pin_commit = commit
else:
pin_changed = True
break # last_pin_commit is the oldest commit still on the current pin
if not pin_changed:
page += 1

# Step 4: compute days
now_utc = datetime.now(timezone.utc)
if last_pin_commit is None:
# No commits to release.json in the last 30 days — pin is at least 30 days old
days = 30
print("No commits to release.json found in the last 30 days; reporting days=30")
else:
committed_at_str = last_pin_commit["commit"]["committer"]["date"]
committed_at = datetime.fromisoformat(committed_at_str.replace("Z", "+00:00"))
days = (now_utc - committed_at).days
print(f"Last pin commit: {last_pin_commit['sha']} at {committed_at_str}")
print(f"Days since last pin: {days}")

# Write days to GITHUB_OUTPUT for the submit step
github_output = os.environ.get("GITHUB_OUTPUT")
if github_output:
with open(github_output, "a") as f:
f.write(f"days={days}\n")
62 changes: 27 additions & 35 deletions bentoml/tests/test_unit.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# (C) Datadog, Inc. 2025-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from unittest.mock import Mock, patch

import pytest
import requests

from datadog_checks.base.constants import ServiceCheck
from datadog_checks.bentoml import BentomlCheck
from datadog_checks.dev.http import MockResponse
from datadog_checks.dev.utils import get_metadata_metrics

from .common import (
Expand All @@ -21,26 +19,20 @@
def test_bentoml_mock_metrics(dd_run_check, aggregator, mock_http_response):
mock_http_response(file_path=get_fixture_path('metrics.txt'))

with patch('datadog_checks.bentoml.check.BentomlCheck.http') as mock_http:
mock_response = type('MockResponse', (), {'status_code': 200})()
mock_http.get.return_value = mock_response
mock_http.get.return_value.raise_for_status = lambda: None

check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)
check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)

for metric in METRICS:
aggregator.assert_metric(metric)
for metric in METRICS:
aggregator.assert_metric(metric)

for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=1, tags=['test:tag', 'status_code:200'])
for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=1, tags=['test:tag', 'status_code:200'])

aggregator.assert_all_metrics_covered()
assert mock_http.get.call_count == 2
aggregator.assert_metrics_using_metadata(get_metadata_metrics())
aggregator.assert_all_metrics_covered()
aggregator.assert_metric_has_tag('bentoml.service.request.count', 'bentoml_endpoint:/summarize')
aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)
aggregator.assert_all_metrics_covered()
aggregator.assert_metrics_using_metadata(get_metadata_metrics())
aggregator.assert_all_metrics_covered()
aggregator.assert_metric_has_tag('bentoml.service.request.count', 'bentoml_endpoint:/summarize')
aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)


def test_bentoml_mock_invalid_endpoint(dd_run_check, aggregator, mock_http_response):
Expand All @@ -52,22 +44,22 @@ def test_bentoml_mock_invalid_endpoint(dd_run_check, aggregator, mock_http_respo
aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.CRITICAL)


def test_bentoml_mock_valid_endpoint_invalid_health(dd_run_check, aggregator, mock_http_response):
mock_http_response(file_path=get_fixture_path('metrics.txt'))

_err = Mock()
_err.status_code = 500
_http_err = requests.HTTPError("500 Internal Server Error")
_http_err.response = _err
_err.raise_for_status.side_effect = _http_err
def test_bentoml_mock_valid_endpoint_invalid_health(dd_run_check, aggregator, mock_http_response_per_endpoint):
mock_http_response_per_endpoint(
{
'http://bentoml:3000/metrics': [MockResponse(file_path=get_fixture_path('metrics.txt'))],
'http://bentoml:3000//livez': [MockResponse(status_code=500)],
'http://bentoml:3000//readyz': [MockResponse(status_code=500)],
}
)

with patch('datadog_checks.bentoml.check.BentomlCheck.http') as mock_http:
mock_http.get.return_value = _err
check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)

check = BentomlCheck('bentoml', {}, [OM_MOCKED_INSTANCE])
dd_run_check(check)
for metric in METRICS:
aggregator.assert_metric(metric)

for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=0, tags=['test:tag', 'status_code:500'])
for metric in ENDPOINT_METRICS:
aggregator.assert_metric(metric, value=0, tags=['test:tag', 'status_code:500'])

aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)
aggregator.assert_service_check('bentoml.openmetrics.health', ServiceCheck.OK)
4 changes: 0 additions & 4 deletions cisco_aci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,6 @@ The Cisco ACI config `.yaml` file can be set up to collect one or both of the fo
| [send_faultinst_faults][13] | Set to `true` to enable collection of Cisco ACI `faultInst` faults as logs. |
| [send_faultdelegate_faults][14] | Set to `true` to enable collection of Cisco ACI `faultDelegate` faults as logs. |

### Events

The Cisco ACI check sends tenant faults as events.

### Service Checks

See [service_checks.json][8] for a list of service checks provided by this integration.
Expand Down
1 change: 1 addition & 0 deletions kafka_actions/changelog.d/23431.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Preload protobuf well-known types (e.g. `google/protobuf/timestamp.proto`) into the descriptor pool so schemas that reference them deserialize correctly.
Loading
Loading