Conversation
Adds a fire-and-forget inventory reporter that makes the serverless-compat mini-agent appear in Fleet Automation as a serverless_compat_agent row. Supported workloads: azure_function, cloud_function (GCP Gen1). Lambda and Azure Spring Apps are explicitly skipped. Key design points: - Startup report fires immediately; periodic reports every 30 min - Process UUID is stable across all reports from the same process - Bounded retry (3 attempts, exponential backoff) for 429/5xx/transport errors - 4xx rejections are not retried - Gen2 Cloud Run Functions (FUNCTION_TARGET set) are excluded - DD_SERVERLESS_COMPAT_VERSION from language package is preferred over the Rust crate version for serverless_compat_version - DD_SERVERLESS_COMPAT_RUNTIME/RUNTIME_VERSION env vars supported for language package handoff, with fallback to Azure/GCP env vars - UUID absent from agent_metadata (only at payload top level) - platform_version field absent (not in REDAPL schema) - Hostname absent (activates ECS Fargate path in EPRW) - GCP metadata server fallback for Gen1 when region/project absent from env
- Add DD_SERVERLESS_COMPAT_INVENTORY_ENABLED gate (default off); extracted
to is_inventory_enabled() so it can be unit-tested without async runtime
- Fix Azure Function resource_id to lowercase ARM format:
/subscriptions/{sub}/resourcegroups/{rg}/providers/microsoft.web/sites/{name}
(was //microsoft.azure/functionApps/... which doesn't match crawler keys)
- Add unit tests: gate off by default, on when "true", off for any other value
- Add gcp-baseline and azure-baseline minimal profiles (1 workload each) for quick REDAPL wire contract validation with a single resource - Set DD_SERVERLESS_INIT_INVENTORY_ENABLED and DD_SERVERLESS_COMPAT_INVENTORY_ENABLED in env_list() so all runner deploys enable inventory without manual az/gcloud override - Add scripts/serverless-compat-deploy/build.sh: builds the Rust compat binary for x86_64-unknown-linux-musl (required by runner.py before packaging) - Include azure-baseline in Azure manifest resume check
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d22f64d660
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let resource_id = format!( | ||
| "/subscriptions/{}/resourcegroups/{}/providers/microsoft.web/sites/{}", | ||
| sub.to_lowercase(), | ||
| rg.to_lowercase(), | ||
| name.to_lowercase() |
There was a problem hiding this comment.
Include the Azure deployment slot in the resource ID
When the mini-agent runs in a non-production Azure Functions deployment slot, WEBSITE_SITE_NAME still identifies the parent app and the slot is exposed separately through WEBSITE_SLOT_NAME. Building every ID as /sites/{name} therefore makes the slot and production deployment report the same primary key, so their inventory records overwrite/deduplicate into one row. Append /slots/{slot} for non-production slots so each ARM resource has a distinct inventory identity.
Useful? React with 👍 / 👎.
| for attempt in 0..=MAX_RETRIES { | ||
| match do_send(client, &url, api_key, body.clone()).await { |
There was a problem hiding this comment.
Bound the retry loop to three total attempts
When the intake returns 429/5xx or a transport error, the inclusive 0..=MAX_RETRIES range performs four HTTP attempts (0 through 3), despite this change advertising a three-attempt bound; the final transport log also reports only three attempts. At serverless scale this adds an unexpected request and another full request timeout per process during an outage, so either use a three-iteration range or redefine the limit and reporting explicitly as retries rather than attempts.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
🟡 Changes recommended
There are correctness and operability issues in the new inventory reporter (retry attempt semantics vs PR description, inaccurate attempt logging, and several silent error-swallowing/log-level concerns) that should be addressed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a new background “inventory reporter” to the datadog-serverless-compat mini-agent so supported serverless compat workloads (Azure Functions + GCP Gen1 Cloud Functions) can periodically emit an inventory payload to the Datadog metadata intake for Fleet Automation visibility.
Changes:
- Spawns a background Tokio task at startup to run the inventory reporter (gated via
DD_SERVERLESS_COMPAT_INVENTORY_ENABLED=true). - Introduces
inventory.rsto build workload identity + payload and send it with bounded retry and optional GCP metadata-server fallback. - Adds new crate dependencies needed for payload generation and process UUIDs (
serde,serde_json,uuid, and Tokiotime).
File summaries
| File | Description |
|---|---|
| crates/datadog-serverless-compat/src/main.rs | Spawns the inventory reporter task during agent startup. |
| crates/datadog-serverless-compat/src/inventory.rs | Implements inventory gating, identity derivation, payload construction, and send/retry logic + unit tests. |
| crates/datadog-serverless-compat/Cargo.toml | Adds dependencies and enables Tokio time feature to support reporting. |
| Cargo.lock | Updates lockfile for newly added dependencies. |
Review details
Suppressed comments (3)
crates/datadog-serverless-compat/src/inventory.rs:210
- This log message reports the 0-based loop index as an “attempt” count, so it under-reports by 1 (e.g., last attempt logs “after 2 attempts” when 3 total attempts were made). Consider logging
attempt + 1or renaming the field to retries.
warn!(
"inventory: transport error after {attempt} attempts \
(report_reason={report_reason}, error={e})"
);
crates/datadog-serverless-compat/src/inventory.rs:425
- The metadata-server request transport error is dropped via
.ok()?, which makes it hard to understand why region/project resolution failed (you only get a later “identity unavailable” warning). Logging the reqwest error here would make troubleshooting much easier.
.get(&url)
.header("Metadata-Flavor", "Google")
.send()
.await
.ok()?;
crates/datadog-serverless-compat/src/inventory.rs:438
- Reading the metadata-server response body and logging the parsed value currently uses
.ok()?(drops the error) and logs the value at INFO. Consider logging the read error and lowering the value log to DEBUG to avoid leaking project IDs into INFO logs.
let body = resp.text().await.ok()?;
let result = parse(body.trim());
info!("inventory: GCP metadata server {label}: {:?}", result);
result
- Files reviewed: 3/4 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// Maximum retry attempts for transient failures (429, 5xx, transport errors). | ||
| const MAX_RETRIES: u32 = 3; |
…er once, clock warning
…bug, owner_name args)
| @@ -0,0 +1,1043 @@ | |||
| // Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/ | |||
There was a problem hiding this comment.
I think the inventory agent should be in a separate crate. This top level crate (datadog-serverless-compat) pulls in other crates as dependencies and initializes them. Also consider breaking up this file into multiple files with related functions grouped together.
| } | ||
| } | ||
|
|
||
| fn build_azure_function_identity(owner_name: &str) -> (String, String) { |
There was a problem hiding this comment.
There is a library in libdatadog that already parses these values. Can that be used here?
https://github.com/DataDog/libdatadog/blob/main/libdd-common/src/azure_app_services.rs
| } | ||
| } | ||
|
|
||
| fn enrich_azure_function_fields(metadata: &mut serde_json::Value, owner_name: &str) { |
There was a problem hiding this comment.
Same comment as above. Use https://github.com/DataDog/libdatadog/blob/main/libdd-common/src/azure_app_services.rs where appropriate.
|
|
||
| // Runtime: prefer DD_SERVERLESS_COMPAT_RUNTIME (set by language package); | ||
| // fall back to FUNCTIONS_WORKER_RUNTIME injected by Azure. | ||
| let runtime = env::var("DD_SERVERLESS_COMPAT_RUNTIME") |
There was a problem hiding this comment.
Where does DD_SERVERLESS_COMPAT_RUNTIME come from? Is that set in the Serverless Compatibility layer today?
| .ok() | ||
| .filter(|s| !s.is_empty()) | ||
| .or_else(|| { | ||
| env::var("FUNCTIONS_WORKER_RUNTIME") |
There was a problem hiding this comment.
What are the expected values in FUNCTIONS_WORKER_RUNTIME? Do they need to be normalized?
|
|
||
| // Runtime version: prefer DD_SERVERLESS_COMPAT_RUNTIME_VERSION (language package), | ||
| // then FUNCTIONS_WORKER_RUNTIME_VERSION, then language-specific vars. | ||
| let runtime_ver = env::var("DD_SERVERLESS_COMPAT_RUNTIME_VERSION") |
There was a problem hiding this comment.
Where doesDD_SERVERLESS_COMPAT_RUNTIME_VERSION come from? Is that set in the Serverless Compatibility layer today?
| /// Runs the inventory reporter for the lifetime of the mini-agent. | ||
| /// | ||
| /// Sends a startup report immediately, then a periodic report every | ||
| /// [`INVENTORY_INTERVAL`]. Spawned as a background task — never panics, | ||
| /// never blocks agent startup. |
There was a problem hiding this comment.
Should this comment be moved to run_inventory_reporter?
| /// Minimum Datadog agent protocol version accepted by EPRW (7.x.x format). | ||
| /// Used only for HTTP transport headers. The actual Compat version is reported | ||
| /// as `agent_metadata.serverless_compat_version`. | ||
| const AGENT_VERSION: &str = "7.83.0"; |
There was a problem hiding this comment.
This is a version for the Go Agent, why is it included here?
Summary
Adds the
serverless_compat_agentinventory reporter to the shared Compat mini-agent so Azure Functions and GCP Gen1 Cloud Functions appear in Fleet Automation.FUNCTION_TARGETset) are excluded — they belong inserverless_init_agentDD_SERVERLESS_COMPAT_VERSION(set by language package) is the primary compat version;CARGO_PKG_VERSIONis the fallbackDD_SERVERLESS_COMPAT_RUNTIME/DD_SERVERLESS_COMPAT_RUNTIME_VERSIONenv vars supported for future language package handoffagent_metadata(top-level only)platform_versionabsent (not in REDAPL schema)What's not here
No deploy scripts, no test logs, no npm binaries — those stay local or on the prototype branch (
nina/svls-9604-inventory-payload).Related
serverless-compatflavor,serverless_compat_agenttable writesserverless_compat_agenttable