Skip to content

Preview node drains with a read-only, honest drain plan #1584

Closed

Description

@nadaverell

Problem

Draining a node cordons it and evicts eligible pods. Radar currently shows one generic confirmation and immediately mutates the cluster. It does not show which pods it expects to evict or skip, why, or that it sends deleteEmptyDirData: true without an explicit acknowledgement.

Operators need a read-only estimate before committing a destructive operation. It must be honest: pods, PDB budgets, and permissions can change between the preview and the real drain.

Goal

Replace the generic confirmation with a server-backed drain plan. Opening it must not cordon, evict, or otherwise mutate the cluster. The operator reviews the expected effects and explicit options, then starts the existing drain flow, which re-lists and evaluates live state again.

Scope

  1. Extract the current drain eligibility decisions into a pure, reusable classifier in pkg/k8score.

    • Return a per-pod outcome and operator-facing reason.
    • Cover terminal pods, mirror/static pods, DaemonSet pods, unmanaged pods, and pods that use emptyDir.
    • Treat a pod as managed only when it has a controller owner reference.
  2. Add a read-only drain-plan endpoint beside the existing drain endpoint.

    • Use the requesting user’s cluster client and RBAC identity.
    • Accept the same relevant options: force, deleteEmptyDirData, and grace period.
    • Return evaluated options, generation time, summary counts, and per-pod evict, skip, or may-block outcomes with reasons.
    • A matching PDB with currently exhausted disruptionsAllowed is useful evidence, but never a guarantee: the Eviction API and live state are authoritative.
  3. Replace the generic Drain Node confirmation with a focused plan dialog.

    • Fetch and display the plan before enabling the destructive action.
    • Explicitly label the result as an estimate that is re-evaluated on execution.
    • Start with deleteEmptyDirData disabled. Require an explicit acknowledgement before affected pods can be evicted.
    • Keep force-unmanaged-pods explicit and explain it.
  4. Make post-drain results legible: actual evictions, skips, and individual failures.

Important constraints

  • Previewing must make no cluster mutation.
  • Do not represent missing or uncomputed information as zero.
  • Do not claim that a preview proves a drain will succeed.
  • This is not a generic preflight framework and does not change the MCP drain tool.

Starting points

  • pkg/k8score/node_ops.go
  • internal/server/node_handlers.go
  • web/src/api/client.ts
  • packages/k8s-ui/src/components/shared/ResourceActionsBar.tsx

There are no focused drain tests today. Please add unit coverage for the classifier and preview handler, plus frontend coverage that viewing the plan does not mutate the cluster and that emptyDir acknowledgement is required.

Acceptance criteria

  • Opening Drain Node shows a read-only plan; neither cordon nor eviction happens until final confirmation.
  • The plan makes expected eviction, skip, and currently PDB-constrained outcomes legible with reasons.
  • The UI calls the plan an estimate and execution re-evaluates live state.
  • emptyDir data is never silently included; the operator explicitly opts in.
  • Managed-owner detection requires a controller owner reference.
  • Execution reports actual evictions and individual skips/failures.

Activity

  1. alexeymoskalev-devops commented on Sep 8, 2026

    @alexeymoskalev-devops
    Contributor

    Hi @nadaverell, I'd like to take this one.

    I went through the current code before writing this, so here is how I plan to approach it and a few points I'd like to confirm before opening a PR.

    Plan, following the scope in the issue

    1. Classifier in pkg/k8score. Extract the decisions from shouldSkipPod into a pure function that returns a per-pod outcome (evict / skip / may-block) plus an operator-facing reason. Cases: terminal pods, mirror pods, DaemonSet pods, unmanaged pods (only a controller owner reference counts, via metav1.GetControllerOf, matching the rest of the codebase; today hasManagedOwner accepts any owner reference), pods using emptyDir, and pods matched by a PDB whose status.disruptionsAllowed is 0 → may-block. DrainNode gets rewritten on top of the classifier so preview and execution share one code path. Signature stays the same, so the MCP manage_node tool is untouched.
    2. Read-only plan endpoint next to the drain route, in the regular timeout group. Same options as drain. It lists pods and PDBs live through the requesting user's client and mutates nothing. Response: evaluated options, generation time, summary counts, per-pod outcomes with reasons, and an explicit estimate: true. If listing PDBs is forbidden, the response says PDBs were not evaluated instead of reporting zero.
    3. Replace the generic confirmation with a plan dialog in ResourceActionsBar: fetch the plan before enabling Drain, label it as an estimate that is re-evaluated on execution, start with deleteEmptyDirData off and require an explicit acknowledgement, keep force-unmanaged explicit with an explanation.
    4. Make DrainResult carry skipped pods with reasons and per-pod failures (additive fields), and show evicted / skipped / failed in the UI after execution.

    Tests: table-driven unit tests for the classifier; a handler test on a fake clientset asserting the plan endpoint issues no patch/evict actions; frontend tests that opening the plan does not call drain and that pods with emptyDir are excluded without the acknowledgement. Smoke on a kind cluster with a PDB at its budget, an emptyDir pod, a bare pod and a DaemonSet.

    Questions

    • Would you prefer this as two PRs (classifier + endpoint + tests first, then UI + post-drain reporting), to keep reviews small? CONTRIBUTING suggests focused PRs, and the first part is independently useful.
    • For the endpoint: POST /nodes/{name}/drain-plan taking the same JSON body as drain, or GET with query params?
    • For may-block, I intend to report only a matching PDB with disruptionsAllowed == 0 as evidence, never as a guarantee, as the issue says. Fine to leave percentage/multi-PDB reasoning to the Eviction API at execution time?

    If this direction works for you, please assign it to me and I'll get the first PR up within a few days.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions