Skip to content

[ML] Harden post-checkout Vault reads against slow checkouts (retry, logging, token refresh) - #3243

Open
edsavage wants to merge 5 commits into
elastic:mainfrom
edsavage:ml/retry-vault-analytics-token
Open

edsavage wants to merge 5 commits into
elastic:mainfrom
edsavage:ml/retry-vault-analytics-token

Conversation

@edsavage

@edsavage edsavage commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Makes the post-checkout hook's Vault reads resilient to slow Buildkite checkouts, which were red-failing otherwise-green macOS test jobs.

Background

The macOS/Orka agents have recently been seeing abysmal GitHub transfer rates (as low as ~100 KiB/s) caused by a MacStadium firewall issue (packet discards; tracked by Platform Eng Productivity). On ml-cpp PR builds this pushed the macOS-aarch64 checkout from ~2.4 min (Oct 9) to 11–20 min (Oct 13).

The agent's environment hook mints VAULT_TOKEN once, at job start. When the clone takes longer than the token's TTL, the token has expired by the time post-checkout runs, so its secret reads fail with permission denied / invalid token. For the test steps the only read is BUILDKITE_ANALYTICS_TOKEN; leaving it empty makes the test-collector plugin's pre-exit hook exit 1 — failing a job in which all tests passed. (The same expiry silently disables sccache on build steps.)

This is intermittent, tracking the firewall degradation — which is why only macOS, and only on slow-clone days, was affected.

Changes

Built up over a few commits, all scoped to .buildkite/hooks/post-checkout:

  1. Retry each Vault read a few times (handles genuine transient blips).
  2. Log the real Vault error on each failed attempt instead of swallowing it with 2>/dev/null — this is what surfaced the invalid token root cause.
  3. vault_reauth: when a read fails with invalid token (the slow-checkout/expiry symptom), mint a fresh VAULT_TOKEN using the same OIDC/JWT flow as the agent (elastic/vault-buildkite-plugin) and retry. Best-effort — never fails the job if re-auth is unavailable.

All Vault reads in the hook route through the shared helper, so analytics and sccache/other reads all benefit.

Testing

  • Helper logic unit-tested locally: re-auth fires exactly once on invalid token, the read then succeeds, stdout stays clean, and the best-effort fallback never fails when buildkite-agent/vault are unavailable.
  • CI: build passes on all platforms. The fast-clone path is confirmed non-regressive; the re-auth recovery path engages only on a genuinely slow clone (intermittent, firewall-driven).

The real root-cause fix (firewall upgrade) is infra-owned; this change makes our builds resilient in the meantime and to any future slow-checkout episode.

A transient Vault failure (e.g. a spurious 403) when reading the test
analytics token left BUILDKITE_ANALYTICS_TOKEN unset. The test-collector
plugin's pre-exit hook hard-requires that variable, so it failed the job
with exit 1 even after a fully green test run.

Route the analytics-token lookup through a small retry helper so an
intermittent Vault error no longer fails an otherwise-passing build, and
collapse the per-platform if/elif chain into a single case mapping.
@elasticsearchmachine

Copy link
Copy Markdown

Pinging @elastic/ml-core (Team:ML)

edsavage and others added 3 commits October 14, 2026 09:26
Apply vault_read_field_with_retry to the build-timings analytics
credentials, the sccache GCS key, the Hugging Face token and the docker
registry credentials. The docker registry read in particular had no
fallback, so a transient Vault error would hard-fail build_pytorch_docker_image
in the same way the analytics-token read failed the macOS test job.
The retry helper swallowed Vault errors with 2>/dev/null, which hid the
real cause when a read ultimately failed (e.g. the macOS analytics-token
read). Capture each attempt's stderr and log it, distinguishing a
permission-denied (403) from a connection/timeout error, while keeping
the field value clean on stdout for the caller.

Co-authored-by: Cursor <cursoragent@cursor.com>
On very slow macOS/Orka checkouts (GitHub transfer rates have collapsed to
~100 KiB/s due to a MacStadium firewall issue, making clones take 20+ min),
the VAULT_TOKEN minted at job start expires before post-checkout runs, so
secret reads fail with 'permission denied / invalid token'. When the retry
helper sees 'invalid token', it now mints a fresh token via the same
OIDC/JWT flow the agent uses (vault_reauth) and retries, so the read
succeeds regardless of how long the clone took. Best-effort: never fails
the job if re-auth is unavailable.

Co-authored-by: Cursor <cursoragent@cursor.com>
@edsavage edsavage changed the title [ML] Retry Vault read of the Buildkite test analytics token [ML] Harden post-checkout Vault reads against slow checkouts (retry, logging, token refresh) Oct 14, 2026
@edsavage
edsavage requested a balanced review from Copilot October 14, 2026 02:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Command substitution prevents the refreshed Vault token from propagating to the hook’s parent shell.

1 open finding
What changed in this PR

Hardens Buildkite post-checkout Vault access against expired tokens and transient failures.

Changes:

  • Adds retry/error logging for Vault reads.
  • Adds OIDC-based Vault reauthentication.
  • Routes secret reads through a shared helper.
File Description
.buildkite/​hooks/​post-checkout Adds resilient Vault authentication and reads.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

pset="${BUILDKITE_ORGANIZATION_SLUG}-${BUILDKITE_PIPELINE_SLUG}-pipeline"
tok=$(vault write -field=token auth/buildkite-oidc/login role="${pset}" jwt="${oidc}" 2>/dev/null) || return 1
[ -n "${tok}" ] || return 1
export VAULT_TOKEN="${tok}"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in cac6d57. The helper now takes an output-variable name and assigns via printf -v, and is called in the parent shell rather than inside $(...). A VAULT_TOKEN refreshed by vault_reauth now propagates to the hook, so multi-secret steps re-authenticate at most once (subsequent reads reuse the fresh token) and later hook commands see the refreshed token.

Addresses review feedback: the retry helper was called inside $(...)
command substitution, so a VAULT_TOKEN refreshed by vault_reauth was lost
when the subshell exited -- multi-secret steps re-authenticated once per
field and later hook commands kept the expired token. The helper now takes
an output-variable name and assigns via printf -v, and is invoked in the
parent shell, so a single refresh propagates to the hook and to subsequent
reads (which reuse the fresh token).

Co-authored-by: Cursor <cursoragent@cursor.com>
@edsavage edsavage added v9.5.6 Release version v9.5.6 v9.4.9 Release version v9.4.9 v8.19.24 Release version v8.19.24 auto-backport Automatically merge backport PRs when CI passes labels Oct 14, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-backport Automatically merge backport PRs when CI passes >build :ml v8.19.24 Release version v8.19.24 v9.4.9 Release version v9.4.9 v9.5.6 Release version v9.5.6 v9.6.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants