Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-alerts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: tj-actions/changed-files@v47
with:
dir_names: "true"
dir_names_max_depth: "2"
dir_names_max_depth: "3"
files: |
helm/bundles/**
Expand Down
7 changes: 7 additions & 0 deletions api/scheduling/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ type Options struct {
SkipHistory bool `json:"skip_history,omitempty"`
// SkipInflight skips creating pessimistic blocking reservations for returned candidates.
SkipInflight bool `json:"skip_inflight,omitempty"`
// SkipCommittedResourceTracking skips writing the placed VM UUID into the matching
// committed resource reservation slot. Set for non-VM-placement runs (capacity checks,
// failover scheduling, CR slot scheduling) that must not modify reservation allocations.
SkipCommittedResourceTracking bool `json:"skip_committed_resource_tracking,omitempty"`
}

// Validate checks for mutually exclusive or inconsistent option combinations.
Expand All @@ -41,5 +45,8 @@ func (o Options) Validate() error {
if o.ReadOnly && !o.SkipInflight {
return errors.New("read-only runs cannot create inflight reservations")
}
if o.ReadOnly && !o.SkipCommittedResourceTracking {
return errors.New("read-only runs must not write CR reservation allocations: set SkipCommittedResourceTracking=true")
}
return nil
}
3 changes: 2 additions & 1 deletion api/scheduling/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ func TestOptions_Validate(t *testing.T) {
wantErr bool
}{
{"zero value is valid", Options{}, false},
{"read-only run, skipping history and inflight", Options{ReadOnly: true, SkipHistory: true, SkipInflight: true}, false},
{"read-only run, skipping all writes", Options{ReadOnly: true, SkipHistory: true, SkipInflight: true, SkipCommittedResourceTracking: true}, false},
{"ReadOnly without SkipHistory is invalid", Options{ReadOnly: true}, true},
{"ReadOnly without SkipInflight is invalid", Options{ReadOnly: true, SkipHistory: true}, true},
{"ReadOnly without SkipCommittedResourceTracking is invalid", Options{ReadOnly: true, SkipHistory: true, SkipInflight: true}, true},
}

for _, tt := range tests {
Expand Down
17 changes: 17 additions & 0 deletions api/v1alpha1/committed_resource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,23 @@ type CommittedResource struct {
Status CommittedResourceStatus `json:"status,omitempty,omitzero"`
}

// IsActive reports whether the commitment spec has active Reservation slots
// (state is confirmed or guaranteed).
func (s *CommittedResourceSpec) IsActive() bool {
return s.State == CommitmentStatusConfirmed || s.State == CommitmentStatusGuaranteed
}

// IsActive reports whether the commitment has active Reservation slots
// (state is confirmed or guaranteed).
func (c *CommittedResource) IsActive() bool {
return c.Spec.IsActive()
}

// MatchesGroup reports whether the commitment targets the given project and flavor group.
func (c *CommittedResource) MatchesGroup(projectID, flavorGroup string) bool {
return c.Spec.ProjectID == projectID && c.Spec.FlavorGroupName == flavorGroup
}

// +kubebuilder:object:root=true

// CommittedResourceList contains a list of CommittedResource
Expand Down
70 changes: 70 additions & 0 deletions api/v1alpha1/committed_resource_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright SAP SE
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import "testing"

func TestCommittedResourceSpec_IsActive(t *testing.T) {
tests := []struct {
state CommitmentStatus
want bool
}{
{CommitmentStatusConfirmed, true},
{CommitmentStatusGuaranteed, true},
{CommitmentStatusPlanned, false},
{CommitmentStatusPending, false},
{CommitmentStatusSuperseded, false},
{CommitmentStatusExpired, false},
}
for _, tt := range tests {
spec := CommittedResourceSpec{State: tt.state}
if got := spec.IsActive(); got != tt.want {
t.Errorf("IsActive() with state %q = %v, want %v", tt.state, got, tt.want)
}
}
}

func TestCommittedResource_IsActive(t *testing.T) {
tests := []struct {
state CommitmentStatus
want bool
}{
{CommitmentStatusConfirmed, true},
{CommitmentStatusGuaranteed, true},
{CommitmentStatusPlanned, false},
{CommitmentStatusPending, false},
{CommitmentStatusSuperseded, false},
{CommitmentStatusExpired, false},
}
for _, tt := range tests {
cr := CommittedResource{Spec: CommittedResourceSpec{State: tt.state}}
if got := cr.IsActive(); got != tt.want {
t.Errorf("IsActive() with state %q = %v, want %v", tt.state, got, tt.want)
}
}
}

func TestCommittedResource_MatchesGroup(t *testing.T) {
cr := CommittedResource{
Spec: CommittedResourceSpec{
ProjectID: "proj-1",
FlavorGroupName: "kvm_v2",
},
}
tests := []struct {
project string
group string
want bool
}{
{"proj-1", "kvm_v2", true},
{"proj-X", "kvm_v2", false},
{"proj-1", "kvm_v3", false},
{"proj-X", "kvm_v3", false},
}
for _, tt := range tests {
if got := cr.MatchesGroup(tt.project, tt.group); got != tt.want {
t.Errorf("MatchesGroup(%q, %q) = %v, want %v", tt.project, tt.group, got, tt.want)
}
}
}
5 changes: 5 additions & 0 deletions api/v1alpha1/reservation_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ type ReservationList struct {
Items []Reservation `json:"items"`
}

// MatchesGroup reports whether the reservation targets the given project and resource group.
func (s *CommittedResourceReservationSpec) MatchesGroup(projectID, resourceGroup string) bool {
return s.ProjectID == projectID && s.ResourceGroup == resourceGroup
}

// IsReady returns true if the reservation has the Ready condition set to True.
func (r *Reservation) IsReady() bool {
return meta.IsStatusConditionTrue(r.Status.Conditions, ReservationConditionReady)
Expand Down
28 changes: 28 additions & 0 deletions api/v1alpha1/reservation_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright SAP SE
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import "testing"

func TestCommittedResourceReservationSpec_MatchesGroup(t *testing.T) {
spec := CommittedResourceReservationSpec{
ProjectID: "proj-1",
ResourceGroup: "kvm_v2",
}
tests := []struct {
project string
group string
want bool
}{
{"proj-1", "kvm_v2", true},
{"proj-X", "kvm_v2", false},
{"proj-1", "kvm_v3", false},
{"proj-X", "kvm_v3", false},
}
for _, tt := range tests {
if got := spec.MatchesGroup(tt.project, tt.group); got != tt.want {
t.Errorf("MatchesGroup(%q, %q) = %v, want %v", tt.project, tt.group, got, tt.want)
}
}
}
8 changes: 7 additions & 1 deletion cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,16 @@ func main() {
}

if slices.Contains(mainConfig.EnabledControllers, "nova-pipeline-controllers") {
featureGates := conf.GetConfigOrDie[nova.FeatureGates]()
// Filter-weigher pipeline controller setup.
filterWeigherController := &nova.FilterWeigherPipelineController{
Monitor: filterWeigherPipelineMonitor,
Monitor: filterWeigherPipelineMonitor,
FeatureGates: featureGates,
NoHostFoundCounter: nova.NewNoHostFoundCounter(),
PlacementCounter: nova.NewPlacementCounter(),
}
metrics.Registry.MustRegister(filterWeigherController.NoHostFoundCounter)
metrics.Registry.MustRegister(filterWeigherController.PlacementCounter)
// Inferred through the base controller.
filterWeigherController.Client = multiclusterClient
if err := filterWeigherController.SetupWithManager(mgr, multiclusterClient); err != nil {
Expand Down
12 changes: 9 additions & 3 deletions helm/bundles/cortex-nova/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,21 @@ cortex-scheduling-controllers:
- failover-reservations-controller
- quota-controller
- capacity-controller
enabledTasks:
- nova-history-cleanup-task
- commitments-sync-task # required for committed resources
# Feature gates control optional capabilities within running components.
featureGates:
# Enables VM allocation write-back into Reservation slots and no-host-found
# classification by committed resource coverage. Enable only on deployments
# that use committed resources. Requires also enabling of CR controllers and tasks
committedResourceTracking: false
# Pipeline used for the empty-state capacity probe (ignores allocations and reservations).
capacityTotalPipeline: "kvm-report-capacity"
# Pipeline used for the current-state capacity probe (considers current VM allocations).
capacityPlaceablePipeline: "kvm-general-purpose-load-balancing-no-history"
# How often the capacity controller re-runs its scheduler probes.
capacityReconcileInterval: 5m
enabledTasks:
- nova-history-cleanup-task
- commitments-sync-task
# If true, the external scheduler API will limit the list of hosts in its
# response to those included in the scheduling request.
novaLimitHostsToRequest: true
Expand Down
Loading