# KEP-10852: Inadmissible Workloads Observability - [Summary](#summary) - [Motivation](#motivation) - [Goals](#goals) - [Non-Goals](#non-goals) - [Proposal](#proposal) - [User Stories](#user-stories) - [Story 1: Operator Alerting for Configuration Errors](#story-1-operator-alerting-for-configuration-errors) - [Story 2: Platform Team Diagnostics and Dashboards](#story-2-platform-team-diagnostics-and-dashboards) - [Story 3: Programmatic Clients and Automation Tools](#story-3-programmatic-clients-and-automation-tools) - [Notes/Constraints/Caveats](#notesconstraintscaveats) - [API and Backwards Compatibility](#api-and-backwards-compatibility) - [Status Patch Co-location for Performance](#status-patch-co-location-for-performance) - [Risks and Mitigations](#risks-and-mitigations) - [API Server Write Volume and Load](#api-server-write-volume-and-load) - [Upgrade / Downgrade & Backwards Compatibility Strategy](#upgrade--downgrade--backwards-compatibility-strategy) - [Upgrade Path](#upgrade-path) - [Downgrade Path](#downgrade-path) - [Design Details](#design-details) - [API Changes](#api-changes) - [Tiered Reason Precedence & Resolution](#tiered-reason-precedence--resolution) - [1. Flavor-Independent (Workload-wide) Reasons](#1-flavor-independent-workload-wide-reasons) - [2. Nominated Flavor Reasons](#2-nominated-flavor-reasons) - [3. Pending Evaluation](#3-pending-evaluation) - [Multi-PodSet Reason Resolution](#multi-podset-reason-resolution) - [Resolution Algorithm](#resolution-algorithm) - [Resolution Examples](#resolution-examples) - [Admitted Condition Initialization and Lifecycle](#admitted-condition-initialization-and-lifecycle) - [DRA Condition Reasons](#dra-condition-reasons) - [DRA QuotaReserved reason](#dra-quotareserved-reason) - [DRA Requeued reason](#dra-requeued-reason) - [Simplification: Removal of NoReservationUnsatisfiedChecks Reason](#simplification-removal-of-noreservationunsatisfiedchecks-reason) - [Prometheus Metrics Schema](#prometheus-metrics-schema) - [Troubleshooting & End-User Inspection](#troubleshooting--end-user-inspection) - [Concrete Status Scenarios (Before & After)](#concrete-status-scenarios-before--after) - [Scenario 1: Newly Created Workload (Initial Reconcile)](#scenario-1-newly-created-workload-initial-reconcile) - [Scenario 2: Waiting for Cluster Capacity (Waiting for Quota)](#scenario-2-waiting-for-cluster-capacity-waiting-for-quota) - [Scenario 3: Configuration Error (Missing Queue)](#scenario-3-configuration-error-missing-queue) - [Scenario 4: Configuration Error (No Matching Flavor)](#scenario-4-configuration-error-no-matching-flavor) - [Future Work](#future-work) - [Test Plan](#test-plan) - [Unit Tests](#unit-tests) - [Integration Tests](#integration-tests) - [Graduation Criteria](#graduation-criteria) - [Beta (v0.19)](#beta-v019) - [GA (v0.20)](#ga-v020) - [Alternatives Considered](#alternatives-considered) - [Introducing a New Separate Status Condition Instead of Updating QuotaReserved Reasons](#introducing-a-new-separate-status-condition-instead-of-updating-quotareserved-reasons) ## Summary This KEP introduces enhanced observability for pending and inadmissible workloads in Kueue. The feature improves diagnostic capabilities by distinguishing configuration-related failures (such as resource flavor mismatches, missing local queues, and requests exceeding maximum limits) from general resource availability waits. It introduces granular, priority-tiered status reasons for when a workload lacks a quota reservation (`QuotaReserved` condition status is `False`), along with a detailed set of Prometheus metrics to monitor and alert on unadmitted workloads. To ensure safe operational rollouts, the functionality is divided behind two separate feature gates: - `UnadmittedWorkloadsObservability`: Controls the overall feature. It enables the granular Prometheus metrics (`kueue_unadmitted_workloads` and `kueue_local_queue_unadmitted_workloads`) and updates status reasons for the `QuotaReserved` and `Admitted` conditions during reconciliations and scheduler cycles to use the new tiered reasons instead of the generic "Pending". - `UnadmittedWorkloadsExplicitStatus`: Gates the immediate, proactive initialization of both `QuotaReserved` and `Admitted` status conditions to `False` (with `PendingEvaluation` and `NoReservation` reasons) during a workload's first reconciliation. * **When enabled:** Both conditions are explicitly written immediately upon creation, ensuring continuous status-based lifecycle tracking. * **When disabled:** Workloads are created with empty status conditions (saving API server write volume under heavy load), and conditions are only updated on subsequent scheduler cycles. ## Motivation Currently, when a workload cannot be admitted due to incompatibility with any available `ResourceFlavor` (e.g., untolerated taints, node affinity mismatch, or incorrect label/selector values), the `QuotaReserved` condition is set to `False` with a generic reason of `Pending`. Using `Pending` as a reason is sub-optimal because a status condition's `Reason` field is intended to provide actionable context for its `Status` (answering the question: "Why is the quota not reserved?"). Saying a condition is `False` due to being `Pending` merely restates the wait state instead of providing the underlying cause. Programmatic clients, dashboard operators, and monitoring tools cannot easily distinguish a configuration-related "flavor mismatch" or "missing queue" state from a workload that is simply waiting for capacity under an otherwise valid configuration. Furthermore, the existing pending workloads metrics (`kueue_pending_workloads` and `kueue_local_queue_pending_workloads`) only categorize pending workloads under two statuses: - `active`: In the admission queue. - `inadmissible`: Failed admission attempt and waiting for cluster conditions to change. Note that the "Inadmissible" condition `Reason` is currently used both for misconfigured queue setups (such as referencing a non-existent queue) and for suspended/inactive queues. **Crucially, workloads referencing a non-existent `LocalQueue` or `ClusterQueue` are not counted in these metrics, as they never successfully enter any queue structure in the manager or cache.** This lack of granularity and the overlapping terminology create substantial operational confusion: 1. Workloads with critical configuration issues (such as referencing a missing queue) are completely omitted from the pending metrics instead of being flagged as problematic. 2. All other valid workloads that are simply waiting for resource availability are grouped together under the same massive, generic `inadmissible` label, making it impossible to distinguish between a healthy capacity wait state and actual configuration errors. This lack of granularity and terminology conflict makes it difficult to detect, alert on, and resolve workloads stuck due to configuration faults versus those that are waiting normally for cluster capacity. ### Goals - Introduce a priority-ordered list of reasons for when the `QuotaReserved` condition status is `False` to separate structural/configuration issues from normal capacity waiting states. - Introduce metrics (`kueue_unadmitted_workloads` and `kueue_local_queue_unadmitted_workloads`) detailing the reasons and underlying causes of why workloads are unadmitted. - Initialize status conditions to `False` on the first reconciliation cycle of a workload to allow continuous lifetime tracking that matches tracking from workload status and the metrics. ### Non-Goals - Modifying the underlying workload admission state machine or scheduling decisions. - Automatically correcting or mutating workload specs when configurations are invalid. - Exposing scheduling queue internals beyond standard condition transitions and aggregated metrics. ## Proposal To address the limitations in diagnostics, the proposal details: 1. Standardized reasons for the `QuotaReserved` condition (when status is `False`) resolved by a priority tier model. 2. Dynamic first-cycle initialization of status conditions to guarantee detailed lifecycle visibility. 3. Decoupling the `Admitted` and `QuotaReserved` lifecycle by removing the hybrid reason `NoReservationUnsatisfiedChecks`. 4. A multi-level metrics reporting schema to provide detailed alerts for common configuration mistakes. ### User Stories #### Story 1: Operator Alerting for Configuration Errors As a cluster operator, I want to create Prometheus alerts when a tenant submits a workload with a typo in their `LocalQueue` label or an invalid toleration configuration that prevents resource flavor matching. Currently, these workloads are either omitted from metrics or included into a generic `inadmissible` group, making specific alerting impossible without writing custom controllers. With the new `kueue_unadmitted_workloads` metric, we can define alerts targeting the `Misconfigured` underlying cause, notifying tenants to fix their configurations immediately. #### Story 2: Platform Team Diagnostics and Dashboards As a platform team, we want to construct a unified Grafana dashboard to track queue queueing characteristics in real-time. We need to distinguish between workloads waiting for normal quota capacity (a standard queueing event) versus workloads blocked because they request resources exceeding maximum limits or because their LocalQueue or ClusterQueue has an active `StopPolicy`. The two-level label scheme (`reason` and `underlying_cause`) allows our team to plot a stacked area chart showing queue metrics broken down by blocking category, improving platform diagnostics. #### Story 3: Programmatic Clients and Automation Tools As a developer building orchestration tools on top of Kueue, I want to programmatically inspect the status of a workload and execute recovery tasks. With distinct, parseable reason tokens like `WaitingForQuota` or `Misconfigured` instead of the generic `Pending` status string, programmatic clients can take separate, automated actions (such as auto-canceling misconfigured jobs or scaling up node groups for capacity-bound jobs) without needing fragile regex matches on the condition's message text. ### Notes/Constraints/Caveats #### API and Backwards Compatibility Replacing the legacy `Pending` reason token for the `QuotaReserved` condition with granular tokens might affect external programmatic clients that strictly string-match on the `Pending` reason. While the condition status (`False`) remains unchanged, this change presents a potential risk for legacy client integrations. To minimize this risk: - The feature is isolated behind feature gates. Although the gates are enabled by default starting in the initial Beta milestone (v0.19), operators can disable them to revert to legacy behavior if needed. - External systems are encouraged to match on condition status first, and use standard reason tokens as a supplementary classification. #### Status Patch Co-location for Performance To prevent double-patching and keep API request count minimal, first-cycle status writes are co-located within existing update routines: - **No Admission Checks**: The reconciler initializes both conditions to `False` in exactly one unified status patch. - **With Admission Checks**: The registration of pending admission checks and the initialization of the `False` status conditions are batched together into a single API transaction. ### Risks and Mitigations #### API Server Write Volume and Load Initializing conditions to `False` on the first reconciliation cycle of newly created workloads increases the total number of API writes per workload, which could generate significant API server load and database write spikes under high workload submission velocities. To mitigate this concern: - The status initialization logic is decoupled from the main metric gates and placed behind a distinct feature gate (`UnadmittedWorkloadsExplicitStatus`). - This separation allows operators to disable explicit status write overhead under heavy load environments while still benefiting from real-time Prometheus metrics. - Performance scaling validation will be monitored and analyzed using early feedback from the opt-in backport to v0.18. ## Upgrade / Downgrade & Backwards Compatibility Strategy ### Upgrade Path Upon enabling the feature gates, existing workloads will have their status reasons updated to the new tiered values during their next reconciliation or scheduler cycle. ### Downgrade Path Disabling the feature gates will cause Kueue to revert to using the generic "Pending" reason. Existing metrics series for unadmitted workloads will stop being updated. ## Design Details ### API Changes The following new condition reason constants are introduced in the API package: ```go const ( // WorkloadNoMatchingFlavor indicates that the workload cannot be scheduled // because no resource flavor matches its nodeSelector or taints. WorkloadNoMatchingFlavor = "NoMatchingFlavor" // WorkloadExceedsMaxQuota indicates that the workload requests resources // exceeding the maximum capacity limits of the ClusterQueue or Cohort. // This also includes exceeding ClusterQueue nominal + borrowingLimit constraints. WorkloadExceedsMaxQuota = "ExceedsMaxQuota" // WorkloadWaitingForQuota indicates that the workload is waiting for // sufficient unused quota to become available in the ClusterQueue/Cohort. WorkloadWaitingForQuota = "WaitingForQuota" // WorkloadTopologyPlacementFailed indicates that the workload has topology // requirements that cannot be satisfied with the current cluster topology usage. WorkloadTopologyPlacementFailed = "TopologyPlacementFailed" // WorkloadWaitingForPreemptedWorkloads indicates that the workload is waiting // for preempted workloads to release their quota. WorkloadWaitingForPreemptedWorkloads = "WaitingForPreemptedWorkloads" // WorkloadMisconfigured indicates that the workload is inadmissible due to // misconfiguration, such as missing LocalQueue or ClusterQueue. WorkloadMisconfigured = "Misconfigured" // WorkloadSuspended indicates that the workload is inadmissible because // the LocalQueue or ClusterQueue StopPolicy is active. WorkloadSuspended = "Suspended" // WorkloadPendingEvaluation indicates that the workload is pending evaluation in the scheduling queue. WorkloadPendingEvaluation = "PendingEvaluation" // WorkloadOnHold indicates that the workload's quota reservation is intentionally released. WorkloadOnHold = "OnHold" // WorkloadAdmittedReasonNoReservation indicates that the workload has no reservation. WorkloadAdmittedReasonNoReservation = "NoReservation" // WorkloadAdmittedUnsatisfiedAdmissionChecks indicates that the workload has not all checks ready. WorkloadAdmittedUnsatisfiedAdmissionChecks = "UnsatisfiedAdmissionChecks" // WorkloadAdmittedPendingDelayedTopologyRequests indicates that there are pending delayed topology requests. WorkloadAdmittedPendingDelayedTopologyRequests = "PendingDelayedTopologyRequests" ) ``` ### Tiered Reason Precedence & Resolution To separate different blocks and keep the status field highly structured, when the `QuotaReserved` status is `False`, the controller will resolve the reason field according to a strict priority hierarchy. Lower tier numbers represent structural failures and block scheduling early, thus taking absolute precedence over higher tiers. Tiers are evaluated at three levels of precedence: #### 1. Flavor-Independent (Workload-wide) Reasons These blockers are independent of flavor assignment and take absolute precedence over any nominated flavor checks. If any of these are active, flavor assignment is not evaluated or reported. | Precedence | Reason Token | Description / Scenario | Location in Memory | | :--- | :--- | :--- | :--- | | **1** | `Deactivated` | Workload is explicitly deactivated (`spec.active: false`). | None | | **2** | `OnHold` | Workload's quota reservation is intentionally released (e.g. StatefulSet scale-to-zero). | None | | **3** | `DRAResourcesUnresolved` | DRA resources could not be resolved during workload preprocessing. | None | | **4** | `Misconfigured` | Workload points to a non-existent or invalid queue, or has invalid workload configuration. | None | | **5** | `NoMatchingFlavor` | Workload requests a flavor that does not exist or whose taints it does not tolerate. | `ClusterQueue.inadmissibleWorkloads` | | **6** | `Suspended` | Administrative hold (the queue's StopPolicy is active). | None | | **7** | `AdmissionGated` | Gated state (AdmissionGatedBy annotation). | None | | **8** | `WaitingForPodsReady` | Scheduling hold (waiting for previously admitted workloads to reach PodsReady under `waitForPodsReady` configuration). | blocks and waits | #### 2. Nominated Flavor Reasons These blockers apply to the nominated flavor assignment selected by the scheduler. Lower numbers represent more severe blocks. | Precedence | Reason Token | Description / Scenario | Location in Memory | | :--- | :--- | :--- | :--- | | **1** | `ExceedsMaxQuota` | Workload requests resource quantities exceeding the absolute maximum allowed capacity (ClusterQueue nominal limits when no cohort, Cohort's total maximum capacity limits, or the ClusterQueue nominal + borrowingLimit constraint). | `ClusterQueue.inadmissibleWorkloads` | | **2** | `WaitingForQuota` | General capacity wait (total unused nominal capacity in the ClusterQueue/Cohort is less than the workload request). | `ClusterQueue.inadmissibleWorkloads` | | **3** | `TopologyPlacementFailed` | TAS workload only. Nominal capacity exists, but capacity is fragmented across domains preventing contiguous placement. | `ClusterQueue.inadmissibleWorkloads` | | **4** | `WaitingForPreemptedWorkloads` | Evictions have been issued; waiting for victims to terminate and release their capacity. | `ClusterQueue.heap` | *Note on preemption transition dynamics*: Between the moment evictions are issued and the victims actually terminate, subsequent scheduler evaluation cycles may see the same workload as `WaitingForQuota` (quota not yet freed) rather than `WaitingForPreemptedWorkloads`. The reason can oscillate until victims release quota. #### 3. Pending Evaluation This is the lowest precedence state, applicable to workloads that are active in the queue but have not yet been evaluated by the scheduler. | Precedence | Reason Token | Description / Scenario | Location in Memory | | :--- | :--- | :--- | :--- | | **1** | `PendingEvaluation` | Workload is active in the queue, awaiting initial scheduler evaluation. | `ClusterQueue.heap` | #### Multi-PodSet Reason Resolution If a workload defines multiple podsets, flavor assignment is evaluated sequentially by podset groups. If a podset group fails to be assigned a flavor, the scheduler exits early, skipping subsequent groups. Consequently, the representative reason for the workload is resolved as the most severe reason among the first failing podset group's reason and any preceding groups' reasons (as shown in Example 2 below). #### Resolution Algorithm The final reason reported in the workload's `QuotaReserved` condition is resolved as follows: 1. **Step 1: Check Flavor-Independent Blockers** If any Flavor-Independent blocker is active (e.g., the workload is deactivated, misconfigured, has no matching flavors, is suspended, gated, or waiting for pods ready), the blocker with the **highest precedence** (lowest number in the Flavor-Independent table) is selected. * *Note on `NoMatchingFlavor`*: This is determined if flavor matching fails for all attempted flavor combinations during the scheduling cycle. 2. **Step 2: Evaluate Nominated Flavor (if no Flavor-Independent blockers match)** If no Flavor-Independent blockers apply, Kueue reports the reason for the nominated flavor assignment (the candidate assignment closest to being runnable, which Kueue selected during the flavor assignment phase). * If the workload defines multiple podsets, this representative reason is resolved to the **most severe reason** among all evaluated podsets (as detailed in Multi-PodSet Reason Resolution). 3. **Step 3: Default to Pending Evaluation** If the workload has not yet been evaluated by the scheduler (e.g., newly created), the reason defaults to `PendingEvaluation`. *Note on Scheduling Equivalence Hashing (Reason Staleness)*: If `SchedulingEquivalenceHashing` is enabled (the default setting) and a workload's scheduling hash is recorded in `noFitSchedulingHashes` (indicating that an equivalent workload recently failed to schedule), any newly queued or updated equivalent workload bypasses the scheduler and is placed directly into the inadmissible workloads list. Because these workloads bypass scheduler evaluation entirely, their status conditions are not updated and they retain their existing reason (e.g. `PendingEvaluation` for new workloads, or their previous failure reason), creating a temporary staleness window until a cluster event clears the equivalence hashes. #### Resolution Examples * **Example 1 (Flavor-Independent precedence resolution)**: A workload is deactivated (`spec.active: false`, Precedence 1) and points to a suspended ClusterQueue (`Suspended`, Precedence 4). - **Result**: `Deactivated` is reported. Although the workload is bypassed by the scheduler queue and not read from the ClusterQueue, the workload controller still reconciles it to update status conditions. Since `Deactivated` (Precedence 1) has higher precedence than `Suspended` (Precedence 4), the controller reports `Deactivated`. * **Example 2 (Multi-PodSet Precedence Resolution)**: A workload defines two podsets (`podset-a` and `podset-b`) in different podset groups (evaluated sequentially). - **First Step**: `podset-a` is evaluated first. It is assigned a flavor that requires preemption, so its status is set to `Preempt` (with reason `WaitingForPreemptedWorkloads`, Precedence 4). Since it successfully received a flavor assignment, the scheduler continues to the next group. - **Second Step**: `podset-b` is evaluated next. All of its candidate flavors are blocked because they request resources exceeding limits, so its status is set to `NoFit` (with reason `ExceedsMaxQuota`, Precedence 1). Because this podset failed flavor assignment, the scheduler exits early. - **Result**: `ExceedsMaxQuota` is reported as the representative reason for the workload because it is the most severe blocker (Precedence 1 vs Precedence 4) among all evaluated podsets. ### Admitted Condition Initialization and Lifecycle When the `UnadmittedWorkloadsExplicitStatus` feature gate is enabled, both status conditions are explicitly initialized to `False` during reconciliation only if the respective condition is absent from the workload status. This prevents overwriting existing conditions or resetting their timestamps on controller restarts. Their reasons are dynamically resolved based on the workload state and queue parameters: - `QuotaReserved`: `False` (with the reason dynamically resolved according to the Tiered Precedence model, e.g., `PendingEvaluation` for normal active queueing, `Deactivated` if inactive, or `Misconfigured` if queue validation fails early). - `Admitted`: `False` (with the reason dynamically resolved to `NoReservation` on the first cycle). ### DRA Condition Reasons #### DRA `QuotaReserved` reason DRA-specific preprocessing failures set the `QuotaReserved` condition to `False` with reason `DRAResourcesUnresolved`. This identifies that quota reservation failed because the workload's DRA resources could not be resolved. Queue configuration failures continue to use the generic `Misconfigured` or `Suspended` reasons. **Metrics impact**: When `UnadmittedWorkloadsObservability` is enabled, DRA failures that previously surfaced as `underlying_cause=Misconfigured` in the `kueue_unadmitted_workloads` and `kueue_local_queue_unadmitted_workloads` metrics now appear as `underlying_cause=DRAResourcesUnresolved`. Operators with alerts that match only `Misconfigured` (e.g. Story 1) should update their rules to also match `DRAResourcesUnresolved`. #### DRA `Requeued` reason DRA-specific preprocessing failures set the `Requeued` condition to `False` with reason `DRAResourcesUnresolved`. The DRA reconciliation path consumes this reason when deciding whether to transition the workload to `Requeued=True` with reason `DRAResourcesResolved` after the DRA resources become available or resolvable. For backwards compatibility, the controller also recognizes the legacy `Requeued=False` reason `Inadmissible` during recovery. Other requeue reasons, such as `PodsReadyTimeout`, are not treated as DRA failures. #### Simplification: Removal of NoReservationUnsatisfiedChecks Reason To simplify status reasoning and decouple the `QuotaReserved` and `Admitted` condition lifecycles, this design explicitly avoids using the hybrid reason `NoReservationUnsatisfiedChecks` at all when a workload simultaneously lacks a quota reservation and has unsatisfied admission checks. Instead: - A workload without a quota reservation will always have its `Admitted` condition reason set to `NoReservation`, regardless of the state of its admission checks. - Once quota reservation is successfully obtained (`QuotaReserved` status is `True`), if admission checks are still pending, the `Admitted` condition will transition its reason to `UnsatisfiedAdmissionChecks`. ### Prometheus Metrics Schema A set of metrics is introduced to track unadmitted workloads when the `Admitted` condition status is `False`. The metrics are: - `kueue_unadmitted_workloads`: Tracks unadmitted workloads at the ClusterQueue level. It includes the following labels: - `cluster_queue`: The name of the ClusterQueue. - `reason`: Mapped 1:1 to the reason for the `Admitted` condition being `False` (e.g., `NoReservation`, `UnsatisfiedAdmissionChecks`, `PendingDelayedTopologyRequests`). - `underlying_cause`: Mapped 1:1 to the proposed priority reasons for the `QuotaReserved` condition status being `False` (e.g., `PendingEvaluation`, `Misconfigured`, `DRAResourcesUnresolved`, `Suspended`, `WaitingForPodsReady`, `WaitingForQuota`, `AdmissionGated`). - `kueue_local_queue_unadmitted_workloads`: Tracks unadmitted workloads at the LocalQueue level. It includes the following labels: - `name`: The name of the LocalQueue. - `namespace`: The namespace of the LocalQueue. - `cluster_queue`: The name of the ClusterQueue. - `reason`: Mapped 1:1 to the reason for the `Admitted` condition being `False` (e.g., `NoReservation`, `UnsatisfiedAdmissionChecks`, `PendingDelayedTopologyRequests`). - `underlying_cause`: Mapped 1:1 to the proposed priority reasons for the `QuotaReserved` condition status being `False` (e.g., `PendingEvaluation`, `Misconfigured`, `DRAResourcesUnresolved`, `Suspended`, `WaitingForPodsReady`, `WaitingForQuota`, `AdmissionGated`). If a workload has successfully obtained a quota reservation (`QuotaReserved` is `True`), the `underlying_cause` label is left empty (`""`), which indicates that the value in the `reason` label is the root cause for the workload not being admitted. **Handling of Missing or Unset Status Conditions** If the `UnadmittedWorkloadsExplicitStatus` feature gate is disabled, a newly created workload will have no status conditions set. For the purpose of metrics tracking: - The missing `Admitted` condition is assumed to represent `False` with the reason `NoReservation`. - The missing `QuotaReserved` condition is assumed to represent `False` with the reason `PendingEvaluation`. This ensures that pending workloads are fully tracked from the moment of their creation, regardless of whether explicit status initialization is active. When the `reason` label is `NoReservation` (the workload lacks a quota reservation), the `underlying_cause` label maps 1:1 to the `QuotaReserved` condition's reason (covering all 6 precedence tiers). Representative examples of these mapping combinations are detailed below: | Admitted Reason | QuotaReserved Reason | `reason` Label | `underlying_cause` Label | Description / Scenario | | :--- | :--- | :--- | :--- | :--- | | `NoReservation` | `False (WaitingForQuota)` | `NoReservation` | `WaitingForQuota` | Workload is waiting for queue capacity. | | `NoReservation` | `False (Misconfigured)` | `NoReservation` | `Misconfigured` | Workload has structural/configuration errors. | | `NoReservation` | `False (DRAResourcesUnresolved)` | `NoReservation` | `DRAResourcesUnresolved` | DRA resources could not be resolved during workload preprocessing. | | `UnsatisfiedAdmissionChecks` | `True (N/A)` | `UnsatisfiedAdmissionChecks` | `""` | Quota is reserved, but blocked by pending admission checks. | | `PendingDelayedTopologyRequests` | `True (N/A)` | `PendingDelayedTopologyRequests` | `""` | Quota is reserved, but blocked by delayed topology paths. | ### Troubleshooting & End-User Inspection Users can inspect why a workload is pending by looking at the `QuotaReserved` and `Admitted` conditions in the Workload status, as well as the Detailed Workload Status (`.status.admissionChecks`). By using `kubectl get workload`, administrators can access these granular reasons to enable automated troubleshooting scripts and more precise Grafana dashboards, allowing them to quickly distinguish between cluster-wide resource exhaustion and individual workload configuration errors. #### Concrete Status Scenarios (Before & After) Below are detailed before-and-after YAML comparisons highlighting the changes across common scheduling states. ##### Scenario 1: Newly Created Workload (Initial Reconcile) Newly submitted workloads in the scheduling queue awaiting their first evaluation. * **Before KEP-10852:** Both conditions are completely absent from the status: ```yaml status: {} ``` * **After KEP-10852:** Explicitly updated status: ```yaml status: conditions: # QuotaReserved condition is ONLY initialized/present on creation if the # UnadmittedWorkloadsExplicitStatus gate is ENABLED. If disabled, it is absent. - type: QuotaReserved status: "False" reason: PendingEvaluation # Tier 8 Reason: Awaiting initial scheduler evaluation message: "Workload is pending evaluation in the scheduling queue" # Admitted condition is ONLY initialized/present on creation if the # UnadmittedWorkloadsExplicitStatus gate is ENABLED. If disabled, it is absent. - type: Admitted status: "False" reason: NoReservation message: "The workload has no reservation" ``` ##### Scenario 2: Waiting for Cluster Capacity (Waiting for Quota) The workload is structurally valid but must wait because the ClusterQueue has insufficient capacity. * **Before KEP-10852:** Uses generic `Pending` reason and the `Admitted` condition is completely absent: ```yaml status: conditions: - type: QuotaReserved status: "False" reason: Pending message: "couldn't assign flavors to pod set main: insufficient unused quota for cpu in flavor default-flavor, 2 more needed" ``` * **After KEP-10852:** Updated status: ```yaml status: conditions: - type: QuotaReserved status: "False" reason: WaitingForQuota # Tier 6 Reason message: "couldn't assign flavors to pod set main: insufficient unused quota for cpu in flavor default-flavor, 2 more needed" # Admitted condition is ONLY initialized/present if the # UnadmittedWorkloadsExplicitStatus gate is ENABLED. If disabled, it remains absent. - type: Admitted status: "False" reason: NoReservation message: "The workload has no reservation" ``` ##### Scenario 3: Configuration Error (Missing Queue) The workload points to a non-existent queue, blocking scheduling evaluation early. * **Before KEP-10852:** ```yaml status: conditions: - type: QuotaReserved status: "False" reason: Inadmissible message: "LocalQueue local-queue doesn't exist" ``` * **After KEP-10852:** Updated status: ```yaml status: conditions: - type: QuotaReserved status: "False" reason: Misconfigured message: "LocalQueue local-queue doesn't exist" # Admitted condition is ONLY initialized/present if the # UnadmittedWorkloadsExplicitStatus gate is ENABLED. If disabled, it remains absent. - type: Admitted status: "False" reason: NoReservation message: "The workload has no reservation" ``` ##### Scenario 4: Configuration Error (No Matching Flavor) The workload fails to match any resource flavor (e.g., due to unsatisfied node labels/taints or conflicting node selectors/affinity). * **Before KEP-10852:** ```yaml status: conditions: - type: QuotaReserved status: "False" reason: Inadmissible message: "couldn't find matching flavor for pod set main" ``` * **After KEP-10852:** Updated status: ```yaml status: conditions: - type: QuotaReserved status: "False" reason: NoMatchingFlavor message: "couldn't find matching flavor for pod set main" # Admitted condition is ONLY initialized/present if the # UnadmittedWorkloadsExplicitStatus gate is ENABLED. If disabled, it remains absent. - type: Admitted status: "False" reason: NoReservation message: "The workload has no reservation" ``` ### Future Work - **Skip Scheduling Queue for Specific Inadmissible Workloads**: Avoid adding workloads that fail due to a flavor mismatch (`NoMatchingFlavor`) or exceed limits (`ExceedsMaxQuota`) to the `inadmissibleWorkloads` list. Since these workloads cannot become schedulable until a cluster configuration changes (such as creating a missing resource flavor or increasing maximum limits), keeping them out of the active scheduling loop reduces scheduler evaluation overhead. ### Test Plan [x] I/we understand the owners of the involved components may require updates to existing tests to make this code solid enough before committing the changes necessary to implement this enhancement. #### Unit Tests - **Precedence Tier Resolver Tests**: Verify correct classification across all 8 precedence tiers, confirming proper ranking decisions (e.g., structural flavor issues take absolute priority over requeue suspensions). - **Metrics Lifecycle Unit Tests**: Verify dynamic metric updates, correct label resolution under diverse workload states, and correct timeseries cleanup. - **Webhook Status Update Integration Unit Tests**: Verify proper batching of initial condition writes and registration tasks, confirming exactly one single write transaction is performed. #### Integration Tests - **Lifecycle Integration Tests**: Submit various valid and invalid workloads (such as missing queues, unsatisfied limits, and normal capacity wait queues) and assert that: - Status conditions are properly initialized to `False` (when the gate is active). - Condition reasons and metric values are updated to match the expected design matrix. - Deleting or admitting the workloads decrements dynamic metrics back to zero. - **Scale and Load Testing**: Run scale benchmarks (up to 5,000 workloads) to evaluate API server write latency and controller CPU profiles when `UnadmittedWorkloadsExplicitStatus` is enabled. ### Graduation Criteria #### Beta (v0.19) - Feature gates enabled by default. - Unit and integration tests for unadmitted metrics and status conditions. - Initial Alpha backport to v0.18 via cherry-pick release to collect early opt-in feedback. - Validate scale performance of first-cycle status writes (when `UnadmittedWorkloadsExplicitStatus` is enabled) under heavy load to ensure no API server saturation or request latency issues. #### GA (v0.20) - Feature gates locked to true. ## Alternatives Considered ### Introducing a New Separate Status Condition Instead of Updating QuotaReserved Reasons An alternative considered was leaving the existing `QuotaReserved` condition's reason as a generic `Pending` value to eliminate backwards-compatibility risks for older scripts, and instead introducing a separate condition type (such as `QuotaAllocated` or `QuotaAcquisition`) to carry the granular, tiered reasons. - **Pros**: Zero backwards-compatibility risk for legacy scripts that expect the exact string `Pending` on the `QuotaReserved` condition. - **Cons**: Substantially increases status API complexity and introduces condition redundancy. Under Kubernetes API design guidelines, a status condition's `Reason` is explicitly intended to explain the cause of its `Status`. Creating a parallel condition simply to explain the reason of another active condition violates typical API design structures, cluttering the status surface of the Workload resource. Therefore, updating the existing condition reasons with proper documentation and feature gate isolation was chosen as the correct architecture.