Skip to content

kueue: Add queue maintenance mode controls and drain actions - #1252

Open
abhayrajjais01 wants to merge 1 commit into
headlamp-k8s:mainfrom
abhayrajjais01:feat/kueue-queue-maintenance-controls
Open

abhayrajjais01 wants to merge 1 commit into
headlamp-k8s:mainfrom
abhayrajjais01:feat/kueue-queue-maintenance-controls

Conversation

@abhayrajjais01

Copy link
Copy Markdown

Description

This PR introduces interactive Queue Maintenance Mode controls (HoldAndDrain, Hold, and None resume) for ClusterQueue and LocalQueue resources in the Headlamp Kueue plugin.

In Kueue, cluster operators manage queue availability and maintenance via spec.stopPolicy. When taking nodes offline or performing cluster maintenance, operators need direct controls in Headlamp to:

  1. Pause Admissions (Hold): Safely pauses new workload admissions while allowing running workloads to complete normally.
  2. Drain Queue (HoldAndDrain): Pauses new admissions and evicts currently admitted workloads (with safety confirmation dialog).
  3. Resume Operations (None): Re-enables normal workload admission.

What Changes

  • Formatters & Helpers (src/resources/queueControlFormatters.ts):
    • Added getStopPolicyColor, getStopPolicyLabel, and getStopPolicyDescription to provide color-coded chips and operational explanations.
    • Added createStopPolicyPatch for standard Kubernetes spec.stopPolicy patch mutations.
  • Interactive UI Components (src/components/common/QueueMaintenanceControl.tsx):
    • QueueMaintenanceControl: Renders maintenance status badge with color coding (Green: Active, Orange: Paused (Hold), Red: Drain (HoldAndDrain)).
    • Quick action buttons with loading spinners and error alerts.
    • Confirmation modal for disruptive HoldAndDrain actions explaining that in-flight workloads will be evicted.
  • Integration:
    • Integrated QueueMaintenanceControl into ClusterQueueDetail and LocalQueueDetail views.
  • Unit Tests (src/resources/queueControlFormatters.test.ts):
    • Added 7 unit tests verifying color mapping, label generation, patch creation, and fallback handling.

@Ralthos Ralthos 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.

This closes a real gap: spec.stopPolicy is read-only on the LocalQueue page today. The
drain confirmation is the right call.

No access check on the controls

QueueMaintenanceControl goes into extraSections unconditionally, under the
KueueAdminResourceAccess that checks verb="get". A read-only user sees
Drain Queue (HoldAndDrain), confirms, and gets a 403. AuthVisible with authVerb="patch"
would hide the buttons.

LocalQueue also needs a namespace there, or the check asks about patching cluster-wide and
refuses anyone holding the permission through a RoleBinding. That is #1199 again.

cohortName breaks the empty case

-                  value: renderCohortLink(clusterQueue.spec.cohortName),
+                  value: renderCohortLink(clusterQueue.cohortName),

The getter returns this.spec.cohortName || '-', and renderCohortLink guards on
if (!cohortName). Since '-' is truthy, a ClusterQueue with no cohort now renders a link to a
cohort named -. Looks unrelated to the feature, so possibly accidental.

Six strings never reach the catalog

t(getStopPolicyLabel(...)) builds the key at runtime and i18next-parser extracts statically,
so the three labels and three descriptions are absent from translation.json. The parser needs
the literals at the t() call site.

The i18n array is empty

"headlamp": { "i18n": [] } lists no locales, so the locales/en/translation.json added here
may never load. prometheus lists 19, flux lists ["en"].

Implement QueueMaintenanceControl card with AuthVisible RBAC checks,
stopPolicy formatters, static i18n keys, and drain confirmation modal.

Signed-off-by: Abhayraj Jaiswal <abhayraj916146@gmail.com>
@abhayrajjais01
abhayrajjais01 force-pushed the feat/kueue-queue-maintenance-controls branch from 6fbc27d to 1f76e91 Compare August 31, 2026 11:30
@abhayrajjais01

Copy link
Copy Markdown
Author

This closes a real gap: spec.stopPolicy is read-only on the LocalQueue page today. The drain confirmation is the right call.

No access check on the controls

QueueMaintenanceControl goes into extraSections unconditionally, under the KueueAdminResourceAccess that checks verb="get". A read-only user sees Drain Queue (HoldAndDrain), confirms, and gets a 403. AuthVisible with authVerb="patch" would hide the buttons.

LocalQueue also needs a namespace there, or the check asks about patching cluster-wide and refuses anyone holding the permission through a RoleBinding. That is #1199 again.

cohortName breaks the empty case

-                  value: renderCohortLink(clusterQueue.spec.cohortName),
+                  value: renderCohortLink(clusterQueue.cohortName),

The getter returns this.spec.cohortName || '-', and renderCohortLink guards on if (!cohortName). Since '-' is truthy, a ClusterQueue with no cohort now renders a link to a cohort named -. Looks unrelated to the feature, so possibly accidental.

Six strings never reach the catalog

t(getStopPolicyLabel(...)) builds the key at runtime and i18next-parser extracts statically, so the three labels and three descriptions are absent from translation.json. The parser needs the literals at the t() call site.

The i18n array is empty

"headlamp": { "i18n": [] } lists no locales, so the locales/en/translation.json added here may never load. prometheus lists 19, flux lists ["en"].

Hi @Ralthos ,

Thanks for the great feedback! have updated the PR with all your suggestions:

Permission Check: Wrapped the buttons in <AuthVisible authVerb="patch"> and passed the namespace for LocalQueue so users with namespace-scoped permissions aren't blocked
Cohort Link: Fixed ClusterQueue details to pass spec.cohortName so empty cohorts show plain '-' instead of a broken link.
i18n Translations: Updated all status labels and descriptions to use literal strings so i18next-parser extracts them into translation.json.
package.json: Added "i18n": ["en"] to headlamp configuration.

happy to make any further changes if needed

@Ralthos

Ralthos commented Aug 31, 2026

Copy link
Copy Markdown

All four landed cleanly. Thanks for turning it around so fast.

The buttons sit inside AuthVisible with authVerb="patch", and the namespace does the
right thing for both types: it is undefined on a cluster-scoped ClusterQueue, so that check
stays cluster-wide, while LocalQueue gets its own.

Cohort reads spec.cohortName again, so an empty one renders a plain dash. The
cohortName === '-' guard is belt and braces now that the caller is fixed, and harmless either way.

Labels and descriptions are literals at the t() call site, all six are in
translation.json, and "i18n": ["en"] is set. Custom stop policy: {{policy}} is a nice
touch for a value neither map knows.

Formatter tests pass here and tsc is clean.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants