Skip to content

Commit 1ba8060

Browse files
jszwedkowebern
andauthored
chore(ci): Prepare 1.2.3 (#2037)
## Summary Release-prep PR for **ADP 1.2.3** on `releases/1.2.x`. Bumps the version and bundles four fixes. Because 1.2.x is on Rust 1.96.0 (same as `main`), the `fetch_update` fix could be cherry-picked directly rather than needing the `#[allow(deprecated)]` workaround used on 1.1.0. 1. **Honor `site` when `dd_url` equals the default-derived URL** — corresponds to #2028 (`7ec65f2`). The Core Agent's config stream sends `dd_url` at its schema default (`https://app.datadoghq.com`) for every configuration, even when the operator only set `site`, so ADP routed all traffic to the US1 intake and `site` was effectively ignored (#1965). A `dd_url` equal to the default-derived URL is now filtered to `None` at deserialization, letting `site` determine the endpoint. `set_dd_url` bypasses serde and is unaffected. Adapted to the 1.2.x code (no `configured_primary_endpoint`), so the change is confined to the `dd_url` deserializer plus tests. 2. **Unblock the nightly `generate-api-docs` build** — cherry-pick of #2007 (`917e05f`). Newer nightly toolchains deprecate `Atomic*::fetch_update`; under `#![deny(warnings)]`, `cargo +nightly doc` turns that into a hard error. Since 1.2.x uses the 1.96.0 toolchain (where `try_update` is stable), the three call sites switch to `try_update`, matching `main`. (The `fixed_size.rs` hunk from the original commit was dropped — that method doesn't exist on 1.2.x and has no `fetch_update` to fix.) 3. **Update `anyhow`** — cherry-pick of #1945 (`bc51393`). Bumps `anyhow` 1.0.102 → 1.0.103 in `Cargo.lock` to unblock `check deny`. (The `2a7be76` hash is the gh-pages docs artifact for that PR; `bc51393` is the source commit.) 4. **Bump `crossbeam-epoch` to 0.9.20** — fresh fix for RUSTSEC-2026-0204. `crossbeam-epoch 0.9.18` (an invalid pointer dereference in the `fmt::Pointer` impl for `Atomic`/`Shared`) fails `check-deny`. This is a repo-wide issue; the equivalent fix for `main` is #2038. Applied directly here (semver-compatible, lockfile-only bump) rather than cherry-picked, since it landed on the release branches in parallel with the main PR. Plus a `chore(dev): Bump ADP to 1.2.3` commit updating `bin/agent-data-plane/Cargo.toml` and the lockfile. ## Test plan - New unit tests for the `dd_url`/`site` resolution (default filtered → `None`; explicit override wins; `set_dd_url` never filtered; end-to-end `build_primary_endpoint` for both cases). - `make check-deny` passes with the crossbeam-epoch bump. - `cargo check` passes for the affected crates on the pinned `1.96.0` toolchain, including the `try_update` call sites. Fixes #1965. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: webern <matt.briggs@datadoghq.com> Co-authored-by: jesse.szwedko <jesse.szwedko@datadoghq.com>
1 parent 4e26859 commit 1ba8060

7 files changed

Lines changed: 114 additions & 22 deletions

File tree

.github/workflows/docs.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ jobs:
3333
run: |
3434
sudo apt-get update
3535
sudo apt-get install -y protobuf-compiler
36-
- name: Set up Rust Nightly
37-
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16
36+
# `make generate-api-docs` installs and pins the nightly toolchain it needs (see
37+
# RUST_NIGHTLY_VERSION in the Makefile); this step just provides rustup/cargo.
38+
- name: Set up Rust
39+
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17
3840
with:
39-
toolchain: nightly
41+
toolchain: stable
4042
cache: false
4143
rustflags: ""
4244
- name: Generate API documentation

Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ MACOS_TEST_AGENT_INSTALL_DIR ?= /tmp/saluki-dda/datadog-agent
3737
export GO_BUILD_IMAGE ?= golang:1.23-bullseye
3838
export GO_APP_IMAGE ?= ubuntu:24.04
3939

40+
# Pinned nightly toolchain shared by the Miri tests and API-doc generation, both of which rely on
41+
# nightly-only features. Keeping it in one variable ensures the two stay in lockstep; bump here to
42+
# move both at once.
43+
export RUST_NIGHTLY_VERSION ?= nightly-2026-07-05
44+
4045
# Tool configuration.
4146
export AUTOINSTALL ?= true
4247
export CARGO_BIN_DIR := $(shell echo "${HOME}/.cargo/bin")
@@ -486,10 +491,10 @@ check-all: ## Check everything
486491
check-all: check-fmt check-clippy check-docs check-deny check-licenses check-unused-deps generate-api-docs check-features
487492

488493
.PHONY: generate-api-docs
489-
generate-api-docs: check-rust-build-tools
494+
generate-api-docs: check-rust-build-tools ensure-rust-nightly
490495
generate-api-docs: ## Check that API documentation builds without errors
491496
@echo "[*] Checking API documentation build..."
492-
@RUSTDOCFLAGS="--enable-index-page -Zunstable-options" cargo +nightly doc --no-deps -Zrustdoc-map --lib
497+
@RUSTDOCFLAGS="--enable-index-page -Zunstable-options" cargo +$(RUST_NIGHTLY_VERSION) doc --no-deps -Zrustdoc-map --lib
493498

494499
.PHONY: check-clippy
495500
check-clippy: check-rust-build-tools
@@ -567,7 +572,7 @@ test-docs: ## Runs all doctests
567572
test-miri: check-rust-build-tools ensure-rust-miri
568573
test-miri: ## Runs all Miri-specific unit tests
569574
@echo "[*] Running Miri-specific unit tests..."
570-
cargo +nightly-2025-06-16 miri test -p stringtheory
575+
cargo +$(RUST_NIGHTLY_VERSION) miri test -p stringtheory
571576

572577
.PHONY: test-loom
573578
test-loom: check-rust-build-tools
@@ -725,15 +730,20 @@ provision-macos-test-env: ## Installs the pinned Datadog Agent ($(MACOS_TEST_AGE
725730
.PHONY: test-integration-macos-ci
726731
test-integration-macos-ci: build-panoramic build-adp-host provision-macos-test-env test-integration-macos-run ## CI entry point: builds binaries, ensures Agent + cert are provisioned, then runs the `mac`-runtime integration tests
727732

728-
.PHONY: ensure-rust-miri
729-
ensure-rust-miri:
733+
.PHONY: ensure-rust-nightly
734+
ensure-rust-nightly:
730735
ifeq ($(shell command -v rustup >/dev/null || echo not-found), not-found)
731-
$(error "Rustup must be present to install nightly toolchain/Miri component: https://www.rust-lang.org/tools/install")
736+
$(error "Rustup must be present to install the nightly toolchain: https://www.rust-lang.org/tools/install")
732737
endif
733-
@echo "[*] Installing/updating nightly Rust (2025-06-16) and Miri component..."
734-
@rustup toolchain install nightly-2025-06-16 --component miri
738+
@echo "[*] Installing/updating nightly Rust ($(RUST_NIGHTLY_VERSION))..."
739+
@rustup toolchain install $(RUST_NIGHTLY_VERSION) --profile minimal
740+
741+
.PHONY: ensure-rust-miri
742+
ensure-rust-miri: ensure-rust-nightly
743+
@echo "[*] Installing/updating Miri component..."
744+
@rustup component add miri --toolchain $(RUST_NIGHTLY_VERSION)
735745
@echo "[*] Ensuring Miri is setup..."
736-
@cargo +nightly-2025-06-16 miri setup
746+
@cargo +$(RUST_NIGHTLY_VERSION) miri setup
737747

738748
##@ Antithesis
739749

bin/agent-data-plane/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "agent-data-plane"
3-
version = "1.2.2"
3+
version = "1.2.3"
44
edition = { workspace = true }
55
license = { workspace = true }
66
repository = { workspace = true }

lib/saluki-components/src/common/datadog/endpoints.rs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,31 @@ static DD_URL_REGEX: LazyLock<Regex> =
2121

2222
pub const DEFAULT_SITE: &str = "datadoghq.com";
2323

24+
/// The primary endpoint URL that is constructed when both `site` and `dd_url` are at their defaults.
25+
///
26+
/// The Core Agent sends `dd_url` at this value even when the operator only configured `site`.
27+
/// A `dd_url` equal to this constant carries no override intent and must not shadow `site`.
28+
const DEFAULT_PRIMARY_ENDPOINT: &str = "https://app.datadoghq.com";
29+
2430
fn default_site() -> String {
2531
DEFAULT_SITE.to_owned()
2632
}
2733

34+
/// Deserializes an optional `dd_url`, treating the schema-default URL as absent.
35+
///
36+
/// The Core Agent always sends `dd_url` at its schema default (`https://app.datadoghq.com`) even
37+
/// when the operator only configured `site`. Filtering here, at deserialization, ensures that a
38+
/// value equal to the default is treated as `None`, allowing `site` to determine the endpoint. This
39+
/// only affects the serde path; programmatic callers such as `set_dd_url` bypass serde and are
40+
/// unaffected.
41+
fn deserialize_dd_url<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
42+
where
43+
D: serde::Deserializer<'de>,
44+
{
45+
let val = Option::<String>::deserialize(deserializer)?;
46+
Ok(val.filter(|url| url.as_str() != DEFAULT_PRIMARY_ENDPOINT))
47+
}
48+
2849
/// Error type for invalid endpoints.
2950
#[derive(Debug, Snafu)]
3051
#[snafu(context(suffix(false)))]
@@ -147,7 +168,7 @@ pub struct EndpointConfiguration {
147168
/// which are both useful when proxying traffic to an intermediate destination before forwarding to Datadog.
148169
///
149170
/// Defaults to unset.
150-
#[serde(default, alias = "url")]
171+
#[serde(default, alias = "url", deserialize_with = "deserialize_dd_url")]
151172
dd_url: Option<String>,
152173

153174
/// Enables sending data to multiple endpoints and/or with multiple API keys via dual shipping.
@@ -846,4 +867,63 @@ mod tests {
846867
.expect("error calculating override API endpoint");
847868
assert_eq!(expected_endpoint, resolved.endpoint().to_string());
848869
}
870+
871+
#[test]
872+
fn deserialize_dd_url_filters_default_value() {
873+
// The default dd_url (what the Agent sends when operator only configured site) should
874+
// deserialize to None so that site takes precedence.
875+
let config_str = r#"{"api_key": "test-key", "dd_url": "https://app.datadoghq.com"}"#;
876+
let config: EndpointConfiguration = serde_json::from_str(config_str).expect("deserialization should succeed");
877+
assert_eq!(None, config.dd_url);
878+
}
879+
880+
#[test]
881+
fn deserialize_dd_url_preserves_explicit_override() {
882+
// A dd_url that differs from the default should deserialize as-is.
883+
let config_str = r#"{"api_key": "test-key", "dd_url": "https://proxy.internal.example.com:3128"}"#;
884+
let config: EndpointConfiguration = serde_json::from_str(config_str).expect("deserialization should succeed");
885+
assert_eq!(
886+
Some("https://proxy.internal.example.com:3128".to_string()),
887+
config.dd_url
888+
);
889+
}
890+
891+
#[test]
892+
fn set_dd_url_is_not_filtered() {
893+
// Programmatic calls to set_dd_url bypass serde and are never filtered, even if set to the default value.
894+
// This is important for MRF and other override paths that may explicitly set the default URL.
895+
let config_str = r#"{"api_key": "test-key", "site": "datadoghq.eu"}"#;
896+
let mut config: EndpointConfiguration =
897+
serde_json::from_str(config_str).expect("deserialization should succeed");
898+
config.set_dd_url("https://app.datadoghq.com".to_string());
899+
assert_eq!(Some("https://app.datadoghq.com".to_string()), config.dd_url);
900+
}
901+
902+
#[test]
903+
fn site_takes_precedence_when_dd_url_is_default() {
904+
// When dd_url is at its default (sent by Agent with source="default"), site should determine the endpoint.
905+
// This is tested through deserialization so the default-filtering occurs.
906+
let config_str = r#"{"api_key": "test-key", "site": "datadoghq.eu", "dd_url": "https://app.datadoghq.com"}"#;
907+
let config: EndpointConfiguration = serde_json::from_str(config_str).expect("deserialization should succeed");
908+
909+
let resolved = config
910+
.build_primary_endpoint(None)
911+
.expect("error building primary endpoint");
912+
// The site path applies a version prefix, so assert the host resolves to the eu site rather than the
913+
// default US1 intake.
914+
let host = resolved.endpoint().host_str().unwrap();
915+
assert!(host.ends_with("datadoghq.eu"), "expected eu site, got {host}");
916+
}
917+
918+
#[test]
919+
fn explicit_dd_url_overrides_site() {
920+
// A dd_url that diverges from the default should take precedence over site.
921+
let config_str = r#"{"api_key": "test-key", "site": "datadoghq.eu", "dd_url": "https://dogpound.io/"}"#;
922+
let config: EndpointConfiguration = serde_json::from_str(config_str).expect("deserialization should succeed");
923+
924+
let resolved = config
925+
.build_primary_endpoint(None)
926+
.expect("error building primary endpoint");
927+
assert_eq!("dogpound.io", resolved.endpoint().host_str().unwrap());
928+
}
849929
}

lib/saluki-io/src/net/util/retry/policy/rolling_exponential.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ where
122122
// decrease the error count.
123123
let _ = self
124124
.error_count
125-
.fetch_update(AcqRel, Relaxed, |count| Some(count.saturating_sub(factor)));
125+
.try_update(AcqRel, Relaxed, |count| Some(count.saturating_sub(factor)));
126126
}
127127
None => {
128128
debug!("Resetting error count to zero after successful response.");

lib/saluki-metrics/src/test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl GaugeStorage {
4545
impl GaugeFn for GaugeStorage {
4646
fn increment(&self, value: f64) {
4747
self.current
48-
.fetch_update(SeqCst, SeqCst, |v| {
48+
.try_update(SeqCst, SeqCst, |v| {
4949
let new = f64::from_bits(v) + value;
5050
Some(new.to_bits())
5151
})
@@ -54,7 +54,7 @@ impl GaugeFn for GaugeStorage {
5454

5555
fn decrement(&self, value: f64) {
5656
self.current
57-
.fetch_update(SeqCst, SeqCst, |v| {
57+
.try_update(SeqCst, SeqCst, |v| {
5858
let new = f64::from_bits(v) - value;
5959
Some(new.to_bits())
6060
})

0 commit comments

Comments
 (0)