Committed resources reservations don't unlock resources of other reservations#641
Committed resources reservations don't unlock resources of other reservations#641
Conversation
📝 WalkthroughWalkthroughAdds a new scheduling intent constant Changes
Sequence Diagram(s)sequenceDiagram
participant Controller
participant Scheduler
participant API as IntentResolver
participant Filter
Controller->>Scheduler: ScheduleReservationRequest(SchedulerHints: _nova_check_type=reserve_for_committed_resource)
Scheduler->>API: GetIntent(hints)
API-->>Scheduler: ReserveForCommittedResourceIntent
Scheduler->>Filter: EvaluateCapacity(request with CR intent)
Filter->>Filter: Inspect request.GetIntent()
alt intent == ReserveForCommittedResourceIntent
Filter->>Filter: Keep committed reservations locked (do not unlock)
Filter-->>Scheduler: Apply blocking capacity checks
else other intents
Filter->>Filter: Apply normal unlock/skip logic for committed reservations
Filter-->>Scheduler: Proceed with usual selection
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go (1)
815-987: Fold these cases into the existing table-driven suites.This adds a third near-identical setup/assert harness in the file. Merging the CR-intent cases into the existing reservation/ignored-reservation tables would keep the tests shorter and make it easier to centralize the intent value through the exported constant instead of repeating the raw literal.
As per coding guidelines, "Test files should be short and contain only the necessary test cases" and "Use struct-based test cases when applicable, but limit yourself to the most relevant cases."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go` around lines 815 - 987, The new TestFilterHasEnoughCapacity_ReserveForCommittedResourceIntent duplicates the table-driven setup; fold its cases into the existing TestFilterHasEnoughCapacity table(s) by adding the CR-intent scenarios as additional entries (using the same hypervisor/reservation/request fields) instead of a separate test function, remove the duplicated harness and client setup in TestFilterHasEnoughCapacity_ReserveForCommittedResourceIntent, and reference the intent via the exported constant (use the package-level intent constant instead of the raw "reserve_for_committed_resource" literal in newNovaRequestWithIntent calls); ensure you update the cases to set FilterHasEnoughCapacity.Options (FilterHasEnoughCapacityOpts.IgnoredReservationTypes) where needed and keep assertions against FilterHasEnoughCapacity.Run and result.Activations unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go`:
- Around line 107-123: The code treats legacy reservations with
reservation.Spec.Type == "" as committed-resource but then checks
s.Options.IgnoredReservationTypes against reservation.Spec.Type, causing empty
types to bypass the intent logic; fix by normalizing the reservation type into a
local variable before the intent and IgnoredReservationTypes checks (e.g.,
resType := reservation.Spec.Type; if resType == "" { resType =
api.CommittedResourceReservation }) and then use resType in
slices.Contains(s.Options.IgnoredReservationTypes, resType) while keeping the
existing intent check that uses request.GetIntent() and
api.ReserveForCommittedResourceIntent.
---
Nitpick comments:
In `@internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go`:
- Around line 815-987: The new
TestFilterHasEnoughCapacity_ReserveForCommittedResourceIntent duplicates the
table-driven setup; fold its cases into the existing TestFilterHasEnoughCapacity
table(s) by adding the CR-intent scenarios as additional entries (using the same
hypervisor/reservation/request fields) instead of a separate test function,
remove the duplicated harness and client setup in
TestFilterHasEnoughCapacity_ReserveForCommittedResourceIntent, and reference the
intent via the exported constant (use the package-level intent constant instead
of the raw "reserve_for_committed_resource" literal in newNovaRequestWithIntent
calls); ensure you update the cases to set FilterHasEnoughCapacity.Options
(FilterHasEnoughCapacityOpts.IgnoredReservationTypes) where needed and keep
assertions against FilterHasEnoughCapacity.Run and result.Activations unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 91c45b37-2325-4621-a1f4-a41be430cb47
📒 Files selected for processing (4)
api/external/nova/messages.gointernal/scheduling/nova/plugins/filters/filter_has_enough_capacity.gointernal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.gointernal/scheduling/reservations/commitments/controller.go
| // Check if this is a CR reservation scheduling request. | ||
| // If so, we should NOT unlock any CR reservations to prevent overbooking. | ||
| // CR capacity should only be unlocked for actual VM scheduling. | ||
| // IMPORTANT: This check MUST happen before IgnoredReservationTypes check | ||
| // to ensure CR reservation scheduling cannot bypass reservation locking. | ||
| intent, err := request.GetIntent() | ||
| switch { | ||
| case err == nil && intent == api.ReserveForCommittedResourceIntent: | ||
| traceLog.Debug("keeping CR reservation locked for CR reservation scheduling", | ||
| "reservation", reservation.Name, | ||
| "intent", intent) | ||
| // Don't continue - fall through to block the resources | ||
| case slices.Contains(s.Options.IgnoredReservationTypes, reservation.Spec.Type): | ||
| // Check IgnoredReservationTypes AFTER intent check for CR reservations. | ||
| // This ensures CR reservation scheduling always respects existing CR reservations. | ||
| traceLog.Debug("ignoring CR reservation type per config", "reservation", reservation.Name) | ||
| continue |
There was a problem hiding this comment.
Normalize legacy empty CR types before consulting IgnoredReservationTypes.
The enclosing branch still treats "" as a committed-resource reservation for backward compatibility, but this check matches on reservation.Spec.Type directly. With IgnoredReservationTypes: [CommittedResourceReservation], legacy reservations with an empty type will still block capacity unexpectedly.
♻️ Proposed fix
intent, err := request.GetIntent()
+ reservationType := reservation.Spec.Type
+ if reservationType == "" {
+ reservationType = v1alpha1.ReservationTypeCommittedResource
+ }
switch {
case err == nil && intent == api.ReserveForCommittedResourceIntent:
traceLog.Debug("keeping CR reservation locked for CR reservation scheduling",
"reservation", reservation.Name,
"intent", intent)
// Don't continue - fall through to block the resources
- case slices.Contains(s.Options.IgnoredReservationTypes, reservation.Spec.Type):
+ case slices.Contains(s.Options.IgnoredReservationTypes, reservationType):
// Check IgnoredReservationTypes AFTER intent check for CR reservations.
// This ensures CR reservation scheduling always respects existing CR reservations.
traceLog.Debug("ignoring CR reservation type per config", "reservation", reservation.Name)
continue🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go`
around lines 107 - 123, The code treats legacy reservations with
reservation.Spec.Type == "" as committed-resource but then checks
s.Options.IgnoredReservationTypes against reservation.Spec.Type, causing empty
types to bypass the intent logic; fix by normalizing the reservation type into a
local variable before the intent and IgnoredReservationTypes checks (e.g.,
resType := reservation.Spec.Type; if resType == "" { resType =
api.CommittedResourceReservation }) and then use resType in
slices.Contains(s.Options.IgnoredReservationTypes, resType) while keeping the
existing intent check that uses request.GetIntent() and
api.ReserveForCommittedResourceIntent.
| case !s.Options.LockReserved && | ||
| // For committed resource reservations: unlock resources only if: | ||
| // 1. Project ID matches | ||
| // 2. ResourceGroup matches the flavor's hw_version |
There was a problem hiding this comment.
Is there a good way to split up this function into some logically separated subfunctions?
This reverts commit e118cf9.
Test Coverage ReportTest Coverage 📊: 68.3% |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go (1)
99-103:⚠️ Potential issue | 🟡 MinorLegacy reservation type
""not covered byIgnoredReservationTypes.The check at line 100 uses
reservation.Spec.Typedirectly. However, line 107's switch case handles bothv1alpha1.ReservationTypeCommittedResourceand""(empty string for backward compatibility). If an operator configuresIgnoredReservationTypes: [CommittedResourceReservation], legacy reservations withType=""will bypass this check and still block capacity.Consider normalizing the type before this check:
Proposed fix
+ // Normalize legacy empty type to CommittedResource for consistent handling + reservationType := reservation.Spec.Type + if reservationType == "" { + reservationType = v1alpha1.ReservationTypeCommittedResource + } + // Check if this reservation type should be ignored - if slices.Contains(s.Options.IgnoredReservationTypes, reservation.Spec.Type) { - traceLog.Debug("ignoring reservation type", "type", reservation.Spec.Type, "reservation", reservation.Name) + if slices.Contains(s.Options.IgnoredReservationTypes, reservationType) { + traceLog.Debug("ignoring reservation type", "type", reservationType, "reservation", reservation.Name) continue }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go` around lines 99 - 103, The check using reservation.Spec.Type can miss legacy reservations with an empty string; normalize the reservation type before the ignored-types check by mapping "" to the committed-resource constant used in the later switch (i.e., set a local resType variable from reservation.Spec.Type and if resType == "" assign v1alpha1.ReservationTypeCommittedResource), then use slices.Contains(s.Options.IgnoredReservationTypes, resType) and the normalized resType in subsequent logic so legacy reservations are treated consistently with the switch handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go`:
- Around line 99-103: The check using reservation.Spec.Type can miss legacy
reservations with an empty string; normalize the reservation type before the
ignored-types check by mapping "" to the committed-resource constant used in the
later switch (i.e., set a local resType variable from reservation.Spec.Type and
if resType == "" assign v1alpha1.ReservationTypeCommittedResource), then use
slices.Contains(s.Options.IgnoredReservationTypes, resType) and the normalized
resType in subsequent logic so legacy reservations are treated consistently with
the switch handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 74b875d3-238c-4ce3-be59-274d87247267
📒 Files selected for processing (2)
internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.gointernal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go
No description provided.