8000 fix(bridge): report real 1P helm release health and verify release on cold-start hydration by VLukiantsev · Pull Request #990 · google/xmc · GitHub
[go: up one dir, main page]

Skip to content

fix(bridge): report real 1P helm release health and verify release on cold-start hydration - #990

Merged
VLukiantsev merged 4 commits into
fix/cms-component-spec-dependenciesfrom
fix/bridge-1p-health-and-hydration
Sep 28, 2026
Merged

VLukiantsev merged 4 commits into
fix/cms-component-spec-dependenciesfrom
fix/bridge-1p-health-and-hydration

Conversation

@VLukiantsev
@VLukiantsev VLukiantsev commented Sep 25, 2026 •
Copy link
Copy Markdown
Collaborator

Summary

xmc-bridge now reports the real health of 1P components from their Helm release, instead of always reporting "healthy". On a cold start it also checks that the release exists before trusting a stale Ready condition.

Stack 2/4: CMS dependencies (0) → this PR → dependency-gated actuation (C1) → dependency-gated deactivation (C2).

Motivation & Context

Found on a live cluster:

  • Always healthy: BridgeProber.ProbeHealth returned healthy for every 1P component. A Component showed Ready=True while its async helm install failed every 5s.
  • Stale Ready trusted: cold-start hydration treated Ready=True at generation N as "actuated" even though no Helm release existed, so the workload was never installed until the generation changed.

Key Changes

  • HelmActionRunner.Status (action.NewStatus). Action-config setup is shared in newActionConfig.

  • HelmManager.ComponentHealth checks, in order:

    Check Reason Degraded
    Last upgrade failed HelmUpgradeFailed True
    Upgrade in flight HelmUpgradePending False
    No release HelmReleaseNotFound True
    Release left in pending-* HelmReleaseStuck True
    Release not deployed HelmReleaseNotReady True
    Status query failed (transient) HelmStatusUnavailable previous conditions kept
    Release deployed ComponentHealthy False

    Healthy results are cached for 1 minute, and the cache is invalidated on every enqueue or error.

  • In-flight upgrade tracking: a pending mark is set on enqueue and cleared by the worker, with a per-request sequence number. This prevents a false Ready=True while the previous revision still reads deployed.

  • evaluateHealth uses ComponentHealth for 1P components. A transient HelmStatusUnavailable keeps the previous conditions instead of flapping.

  • Hydration distrusts the CR only on ErrReleaseNotFound or a terminal non-deployed status. Transient errors and pending-* keep trusting the CR, which avoids needless revision bumps.

  • Reason constants are exported next to the Condition* constants. The README documents the health semantics, cache, recovery from stuck releases, and that deployed means manifests applied (no --wait), not pod readiness.

Assumptions

  • No automatic recovery for stuck pending-* releases (manual helm rollback/uninstall is documented). — Helm records no lock owner, so an automatic rollback could clobber another actor's operation. — Operators must intervene when a bridge crashes mid-upgrade.
  • A 1-minute cache for healthy results throttles the Helm storage reads on the 5s poll. — Out-of-band deletions are detected within about a minute.

Verification & Testing

  • Commands Executed: bazel test //pkg/components/... //cmd/xmc_bridge/... (9/9 pass), ./.githooks/pre-commit (pass). Size vs parent: 287 prod / 893 total.
  • New tests:
    • ComponentHealth table test;
    • async pending → healthy/failed, with a real async K8sHelmManager in the reconciler test;
    • cold-start hydration: release missing, transient error, pending release;
    • SDK Status against an httptest API server with the memory driver;
    • cache TTL (synctest).
  • Live (GKE ...): after an out-of-band helm uninstall xmc-cmp-ingestion-gateway, the Component reported Ready=False/HelmReleaseNotFound (previously a false Ready=True). After kubectl rollout restart of the bridge, hydration detected the missing release and the bridge reinstalled it; the Component became ENABLED/Ready=True.

Related Issues / Bugs

Found while working on bug 548435582.

TAG=agy
CONV=baca6192-74d3-4579-9f73-0a3263d9a2a7

@VLukiantsev
VLukiantsev added this pull request to stack #993 September 25, 2026 12:12
@VLukiantsev VLukiantsev changed the title fix/bridge 1p health and hydration fix(bridge): report real 1P helm release health and verify release on cold-start hydration Sep 25, 2026
@gemini-code-assist gemini-code-assist Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request implements first-party (1P) component health evaluation and release verification during cold-start state hydration. It introduces the ComponentHealth method to the HelmManager interface, enabling the reconciler to determine health based on actual Helm release status, and updates HydrateComponentState to verify that a Ready=True CR has an actively deployed release. A critical compilation error was identified in helm_manager.go because release.Status does not have an IsPending() method. Additionally, defensive nil-checks are recommended for m.healthVerifiedAt to prevent runtime panics on zero-initialized managers.

Comment thread pkg/components/bridge/helm_manager.go Resolved
@VLukiantsev
Copy link
Copy Markdown
Collaborator Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a robust health checking mechanism for first-party (1P) Helm-managed components. It adds a ComponentHealth method to the HelmManager interface and implements it in K8sHelmManager to report the real-time status of Helm releases (such as deployed, pending, failed, or stuck). The reconciler is updated to evaluate 1P component health using this new mechanism, caching successful verdicts to throttle API calls, and handling transient status errors gracefully. Additionally, cold-start state hydration now verifies the actual release status before trusting a Ready=True custom resource status, forcing a re-actuation if the release is missing or failed. There are no review comments, so no feedback is provided.

@VLukiantsev
VLukiantsev force-pushed the fix/bridge-1p-health-and-hydration branch from b64ea29 to e8168c8 Compare September 25, 2026 12:39
@VLukiantsev
VLukiantsev force-pushed the fix/bridge-1p-health-and-hydration branch from e8168c8 to 3c3d699 Compare September 25, 2026 13:19
@VLukiantsev
VLukiantsev marked this pull request as ready for review September 25, 2026 14:43
@github-actions
github-actions Bot requested a review from janzak-dev September 25, 2026 14:43
@VLukiantsev
VLukiantsev removed this pull request from stack #993 September 25, 2026 14:49
@VLukiantsev
VLukiantsev added this pull request to stack #995 September 25, 2026 14:49
@VLukiantsev
VLukiantsev force-pushed the fix/bridge-1p-health-and-hydration branch 2 times, most recently from d1049d8 to 33ca576 Compare September 28, 2026 08:26
@VLukiantsev VLukiantsev added the priority::2-high top of mind. should not wait. label Sep 28, 2026
janzak-dev
janzak-dev previously approved these changes Sep 28, 2026
Comment thread pkg/components/bridge/reconciler.go Resolved
Comment thread pkg/components/bridge/reconciler.go Resolved
… cold-start hydration

1P components were marked Ready=True/ENABLED unconditionally because
BridgeProber.ProbeHealth returns healthy for every non-3P type, even while
the async Helm install failed on every retry. On cold start, that Ready=True
condition plus the pre-persisted values ConfigMap made HydrateComponentState
record the component as actuated, so a component with no Helm release was
never installed until its generation changed.

- Add HelmActionRunner.Status (action.NewStatus) and extract the shared
  action-config initialization into newActionConfig.
- Add HelmManager.ComponentHealth: reports HelmUpgradeFailed,
  HelmRunnerUninitialized, HelmReleaseNotFound, HelmStatusUnavailable,
  HelmReleaseNotReady, or ComponentHealthy from the real release state.
- evaluateHealth uses ComponentHealth for 1P components when a HelmManager
  is configured; the prober remains the fallback otherwise.
- HydrateComponentState verifies the release is deployed (outside m.mu)
  before trusting a Ready CR as proof of actuation; the deactivation path
  and the deployed-release no-revision-bump guarantee are unchanged.

BUG=b/548435582
TAG=agy
Address review findings on 1P health and cold-start hydration:

- Track async upgrades per component (sequence-guarded) so ComponentHealth
  reports HelmUpgradePending (Ready=False, Degraded=False) instead of the
  previously deployed revision while an upgrade is queued or running.
- Keep previous Ready/Degraded conditions on transient Helm status errors
  (HelmStatusUnavailable) and log a warning.
- Hydration distrusts the CR only on ErrReleaseNotFound or a terminal
  non-deployed status; transient errors and pending-* releases trust it.
  Pending releases without an in-flight upgrade surface as
  HelmReleaseStuck (Degraded=True) with documented manual recovery.
- Cache healthy verdicts for 1 minute, invalidated on upgrade enqueue and
  completion, to throttle Helm Status calls.
- Classify not-found strictly via errors.Is(driver.ErrReleaseNotFound).
- Export condition reason constants from reconciler.go and replace literals.
- README: document 'deployed' as manifests applied (no --wait), new
  reasons, cache, and stuck-release recovery.

BUG=b/548435582
TAG=agy
Initialize healthVerifiedAt on first write, matching upgradeErrs and
pendingUpgrades, so a manager not built via NewK8sHelmManager cannot panic.

BUG=b/548435582
TAG=agy
CONV=baca6192-74d3-4579-9f73-0a3263d9a2a7
@VLukiantsev
VLukiantsev force-pushed the fix/bridge-1p-health-and-hydration branch from 33ca576 to 2279dc4 Compare September 28, 2026 11:15
preserveHealthConditions now replaces or appends via a shared
upsertCondition helper (also used by setCondition), so it cannot emit
duplicate Ready/Degraded entries if res.Conditions is pre-populated.

Comment link: #990 (comment)

BUG=b/548435582
TAG=agy
CONV=baca6192-74d3-4579-9f73-0a3263d9a2a7
@VLukiantsev
VLukiantsev added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit d7f0f91 Sep 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority::2-high top of mind. should not wait.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0