From dea447c8e6c7bfcc69bccdc7bc6e4590932276cc Mon Sep 17 00:00:00 2001
From: henrichter
Date: Sat, 14 Feb 2026 00:50:50 +0100
Subject: [PATCH 1/5] helm: add secret rbac to library chart
---
.../bundles/cortex-cinder/templates/rbac.yaml | 25 -------------------
.../bundles/cortex-manila/templates/rbac.yaml | 25 -------------------
helm/bundles/cortex-nova/templates/rbac.yaml | 25 -------------------
helm/library/cortex/templates/rbac/role.yaml | 7 ++++++
4 files changed, 7 insertions(+), 75 deletions(-)
delete mode 100644 helm/bundles/cortex-cinder/templates/rbac.yaml
delete mode 100644 helm/bundles/cortex-manila/templates/rbac.yaml
delete mode 100644 helm/bundles/cortex-nova/templates/rbac.yaml
diff --git a/helm/bundles/cortex-cinder/templates/rbac.yaml b/helm/bundles/cortex-cinder/templates/rbac.yaml
deleted file mode 100644
index bbe298f12..000000000
--- a/helm/bundles/cortex-cinder/templates/rbac.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-# Cluster level access is required as the controller manager has no default namespace set.
-kind: ClusterRole
-metadata:
- name: cortex-cinder-secret-reader
-rules:
- - apiGroups: [""]
- resources: ["secrets"]
- verbs: ["get", "list", "watch"]
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: cortex-cinder-secret-reader-binding
-subjects:
- - kind: ServiceAccount
- name: cortex-cinder-scheduling-controller-manager
- namespace: {{ .Release.Namespace }}
- - kind: ServiceAccount
- name: cortex-cinder-knowledge-controller-manager
- namespace: {{ .Release.Namespace }}
-roleRef:
- kind: ClusterRole
- name: cortex-cinder-secret-reader
- apiGroup: rbac.authorization.k8s.io
diff --git a/helm/bundles/cortex-manila/templates/rbac.yaml b/helm/bundles/cortex-manila/templates/rbac.yaml
deleted file mode 100644
index a370bfb6d..000000000
--- a/helm/bundles/cortex-manila/templates/rbac.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-# Cluster level access is required as the controller manager has no default namespace set.
-kind: ClusterRole
-metadata:
- name: cortex-manila-secret-reader
-rules:
- - apiGroups: [""]
- resources: ["secrets"]
- verbs: ["get", "list", "watch"]
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: cortex-manila-secret-reader-binding
-subjects:
- - kind: ServiceAccount
- name: cortex-manila-scheduling-controller-manager
- namespace: {{ .Release.Namespace }}
- - kind: ServiceAccount
- name: cortex-manila-knowledge-controller-manager
- namespace: {{ .Release.Namespace }}
-roleRef:
- kind: ClusterRole
- name: cortex-manila-secret-reader
- apiGroup: rbac.authorization.k8s.io
diff --git a/helm/bundles/cortex-nova/templates/rbac.yaml b/helm/bundles/cortex-nova/templates/rbac.yaml
deleted file mode 100644
index 36914f17c..000000000
--- a/helm/bundles/cortex-nova/templates/rbac.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-# Cluster level access is required as the controller manager has no default namespace set.
-kind: ClusterRole
-metadata:
- name: cortex-nova-secret-reader
-rules:
- - apiGroups: [""]
- resources: ["secrets"]
- verbs: ["get", "list", "watch"]
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: cortex-nova-secret-reader-binding
-subjects:
- - kind: ServiceAccount
- name: cortex-nova-scheduling-controller-manager
- namespace: {{ .Release.Namespace }}
- - kind: ServiceAccount
- name: cortex-nova-knowledge-controller-manager
- namespace: {{ .Release.Namespace }}
-roleRef:
- kind: ClusterRole
- name: cortex-nova-secret-reader
- apiGroup: rbac.authorization.k8s.io
diff --git a/helm/library/cortex/templates/rbac/role.yaml b/helm/library/cortex/templates/rbac/role.yaml
index 440de5e31..77ee003a5 100644
--- a/helm/library/cortex/templates/rbac/role.yaml
+++ b/helm/library/cortex/templates/rbac/role.yaml
@@ -61,4 +61,11 @@ rules:
verbs:
- create
- patch
+- apiGroups: [""]
+ resources:
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
{{- end -}}
From b11861fad1ce03233ca0174b5fd4edab87fd1e51 Mon Sep 17 00:00:00 2001
From: henrichter
Date: Sun, 15 Feb 2026 17:26:37 +0100
Subject: [PATCH 2/5] fix[pipeline]: use existing context to propagate
cancellation
---
.../cinder/filter_weigher_pipeline_controller.go | 2 +-
internal/scheduling/lib/filter_monitor.go | 4 ++--
internal/scheduling/lib/filter_monitor_test.go | 6 +++---
internal/scheduling/lib/filter_test.go | 6 +++---
internal/scheduling/lib/filter_validation.go | 4 ++--
internal/scheduling/lib/filter_validation_test.go | 4 ++--
internal/scheduling/lib/filter_weigher_pipeline.go | 14 ++++++++------
.../scheduling/lib/filter_weigher_pipeline_step.go | 2 +-
.../lib/filter_weigher_pipeline_step_monitor.go | 4 +++-
.../filter_weigher_pipeline_step_monitor_test.go | 5 +++--
.../scheduling/lib/filter_weigher_pipeline_test.go | 10 +++++-----
internal/scheduling/lib/weigher_monitor.go | 4 ++--
internal/scheduling/lib/weigher_monitor_test.go | 6 +++---
internal/scheduling/lib/weigher_test.go | 6 +++---
internal/scheduling/lib/weigher_validation.go | 4 ++--
internal/scheduling/lib/weigher_validation_test.go | 8 ++++----
.../machines/filter_weigher_pipeline_controller.go | 2 +-
.../filter_weigher_pipeline_controller_test.go | 2 +-
.../machines/plugins/filters/filter_noop.go | 2 +-
.../machines/plugins/filters/filter_noop_test.go | 2 +-
.../manila/filter_weigher_pipeline_controller.go | 2 +-
.../plugins/weighers/netapp_cpu_usage_balancing.go | 4 ++--
.../weighers/netapp_cpu_usage_balancing_test.go | 2 +-
.../nova/filter_weigher_pipeline_controller.go | 2 +-
.../plugins/filters/filter_aggregate_metadata.go | 4 ++--
.../filters/filter_aggregate_metadata_test.go | 4 ++--
.../plugins/filters/filter_allowed_projects.go | 4 ++--
.../filters/filter_allowed_projects_test.go | 2 +-
.../nova/plugins/filters/filter_capabilities.go | 4 ++--
.../plugins/filters/filter_capabilities_test.go | 4 ++--
.../nova/plugins/filters/filter_correct_az.go | 4 ++--
.../nova/plugins/filters/filter_correct_az_test.go | 2 +-
.../nova/plugins/filters/filter_exclude_hosts.go | 2 ++
.../plugins/filters/filter_exclude_hosts_test.go | 2 +-
.../plugins/filters/filter_external_customer.go | 4 ++--
.../filters/filter_external_customer_test.go | 2 +-
.../plugins/filters/filter_has_accelerators.go | 4 ++--
.../filters/filter_has_accelerators_test.go | 2 +-
.../plugins/filters/filter_has_enough_capacity.go | 6 +++---
.../filters/filter_has_enough_capacity_test.go | 8 ++++----
.../plugins/filters/filter_has_requested_traits.go | 4 ++--
.../filters/filter_has_requested_traits_test.go | 2 +-
.../plugins/filters/filter_host_instructions.go | 3 ++-
.../filters/filter_host_instructions_test.go | 2 +-
.../filters/filter_instance_group_affinity.go | 2 ++
.../filters/filter_instance_group_affinity_test.go | 2 +-
.../filters/filter_instance_group_anti_affinity.go | 3 ++-
.../filter_instance_group_anti_affinity_test.go | 2 +-
.../nova/plugins/filters/filter_live_migratable.go | 3 ++-
.../plugins/filters/filter_live_migratable_test.go | 6 +++---
.../filters/filter_requested_destination.go | 4 ++--
.../filters/filter_requested_destination_test.go | 4 ++--
.../plugins/filters/filter_status_conditions.go | 4 ++--
.../filters/filter_status_conditions_test.go | 2 +-
.../nova/plugins/weighers/kvm_binpack.go | 4 ++--
.../nova/plugins/weighers/kvm_binpack_test.go | 2 +-
.../plugins/weighers/kvm_failover_evacuation.go | 2 +-
.../weighers/kvm_failover_evacuation_test.go | 2 +-
.../weighers/kvm_instance_group_soft_affinity.go | 4 ++--
.../kvm_instance_group_soft_affinity_test.go | 2 +-
.../plugins/weighers/kvm_prefer_smaller_hosts.go | 4 ++--
.../weighers/kvm_prefer_smaller_hosts_test.go | 2 +-
.../vmware_anti_affinity_noisy_projects.go | 4 ++--
.../vmware_anti_affinity_noisy_projects_test.go | 2 +-
.../vmware_avoid_long_term_contended_hosts.go | 4 ++--
.../vmware_avoid_long_term_contended_hosts_test.go | 2 +-
.../vmware_avoid_short_term_contended_hosts.go | 4 ++--
...vmware_avoid_short_term_contended_hosts_test.go | 2 +-
.../nova/plugins/weighers/vmware_binpack.go | 4 ++--
.../nova/plugins/weighers/vmware_binpack_test.go | 2 +-
.../pods/filter_weigher_pipeline_controller.go | 2 +-
.../filter_weigher_pipeline_controller_test.go | 2 +-
.../pods/plugins/filters/filter_node_affinity.go | 2 +-
.../plugins/filters/filter_node_affinity_test.go | 2 +-
.../pods/plugins/filters/filter_node_available.go | 2 +-
.../plugins/filters/filter_node_available_test.go | 2 +-
.../pods/plugins/filters/filter_node_capacity.go | 2 +-
.../plugins/filters/filter_node_capacity_test.go | 2 +-
.../scheduling/pods/plugins/filters/filter_noop.go | 2 +-
.../pods/plugins/filters/filter_noop_test.go | 2 +-
.../pods/plugins/filters/filter_taint.go | 2 +-
.../pods/plugins/filters/filter_taint_test.go | 2 +-
.../scheduling/pods/plugins/weighers/binpack.go | 3 ++-
.../pods/plugins/weighers/binpack_test.go | 2 +-
84 files changed, 148 insertions(+), 135 deletions(-)
diff --git a/internal/scheduling/cinder/filter_weigher_pipeline_controller.go b/internal/scheduling/cinder/filter_weigher_pipeline_controller.go
index 52ec37306..366473690 100644
--- a/internal/scheduling/cinder/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/cinder/filter_weigher_pipeline_controller.go
@@ -121,7 +121,7 @@ func (c *FilterWeigherPipelineController) process(ctx context.Context, decision
return err
}
- result, err := pipeline.Run(request)
+ result, err := pipeline.Run(ctx, request)
if err != nil {
log.Error(err, "failed to run pipeline")
return err
diff --git a/internal/scheduling/lib/filter_monitor.go b/internal/scheduling/lib/filter_monitor.go
index d0afd9282..c3ecc83fe 100644
--- a/internal/scheduling/lib/filter_monitor.go
+++ b/internal/scheduling/lib/filter_monitor.go
@@ -43,6 +43,6 @@ func (fm *FilterMonitor[RequestType]) Validate(ctx context.Context, params v1alp
}
// Run the filter and observe its execution.
-func (fm *FilterMonitor[RequestType]) Run(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
- return fm.monitor.RunWrapped(traceLog, request, fm.filter)
+func (fm *FilterMonitor[RequestType]) Run(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
+ return fm.monitor.RunWrapped(ctx, traceLog, request, fm.filter)
}
diff --git a/internal/scheduling/lib/filter_monitor_test.go b/internal/scheduling/lib/filter_monitor_test.go
index f709d88aa..bf90a0de6 100644
--- a/internal/scheduling/lib/filter_monitor_test.go
+++ b/internal/scheduling/lib/filter_monitor_test.go
@@ -23,7 +23,7 @@ func TestMonitorFilter(t *testing.T) {
InitFunc: func(ctx context.Context, cl client.Client, step v1alpha1.FilterSpec) error {
return nil
},
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{"host1": 0.5, "host2": 1.0},
}, nil
@@ -77,7 +77,7 @@ func TestFilterMonitor_Init(t *testing.T) {
func TestFilterMonitor_Run(t *testing.T) {
runCalled := false
mockFilter := &mockFilter[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
runCalled = true
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{"host1": 0.5, "host2": 1.0},
@@ -100,7 +100,7 @@ func TestFilterMonitor_Run(t *testing.T) {
Weights: map[string]float64{"host1": 0.1, "host2": 0.2, "host3": 0.3},
}
- result, err := fm.Run(slog.Default(), request)
+ result, err := fm.Run(t.Context(), slog.Default(), request)
if err != nil {
t.Errorf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/lib/filter_test.go b/internal/scheduling/lib/filter_test.go
index 652211163..5efecfa9a 100644
--- a/internal/scheduling/lib/filter_test.go
+++ b/internal/scheduling/lib/filter_test.go
@@ -16,7 +16,7 @@ import (
type mockFilter[RequestType FilterWeigherPipelineRequest] struct {
InitFunc func(ctx context.Context, client client.Client, step v1alpha1.FilterSpec) error
ValidateFunc func(ctx context.Context, params v1alpha1.Parameters) error
- RunFunc func(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error)
+ RunFunc func(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error)
}
func (m *mockFilter[RequestType]) Init(ctx context.Context, client client.Client, step v1alpha1.FilterSpec) error {
@@ -31,11 +31,11 @@ func (m *mockFilter[RequestType]) Validate(ctx context.Context, params v1alpha1.
}
return m.ValidateFunc(ctx, params)
}
-func (m *mockFilter[RequestType]) Run(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
+func (m *mockFilter[RequestType]) Run(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
if m.RunFunc == nil {
return &FilterWeigherPipelineStepResult{}, nil
}
- return m.RunFunc(traceLog, request)
+ return m.RunFunc(ctx, traceLog, request)
}
// filterTestOptions implements FilterWeigherPipelineStepOpts for testing.
diff --git a/internal/scheduling/lib/filter_validation.go b/internal/scheduling/lib/filter_validation.go
index 9ad43311d..17c418d5d 100644
--- a/internal/scheduling/lib/filter_validation.go
+++ b/internal/scheduling/lib/filter_validation.go
@@ -35,8 +35,8 @@ func validateFilter[RequestType FilterWeigherPipelineRequest](filter Filter[Requ
}
// Run the filter and validate what happens.
-func (s *FilterValidator[RequestType]) Run(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
- result, err := s.Filter.Run(traceLog, request)
+func (s *FilterValidator[RequestType]) Run(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
+ result, err := s.Filter.Run(ctx, traceLog, request)
if err != nil {
return nil, err
}
diff --git a/internal/scheduling/lib/filter_validation_test.go b/internal/scheduling/lib/filter_validation_test.go
index dc35c2f6a..703f6d458 100644
--- a/internal/scheduling/lib/filter_validation_test.go
+++ b/internal/scheduling/lib/filter_validation_test.go
@@ -146,7 +146,7 @@ func TestFilterValidator_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter := &mockFilter[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
return tt.runResult, tt.runError
},
}
@@ -156,7 +156,7 @@ func TestFilterValidator_Run(t *testing.T) {
}
traceLog := slog.Default()
- result, err := validator.Run(traceLog, request)
+ result, err := validator.Run(t.Context(), traceLog, request)
if tt.expectError && err == nil {
t.Error("expected error but got nil")
diff --git a/internal/scheduling/lib/filter_weigher_pipeline.go b/internal/scheduling/lib/filter_weigher_pipeline.go
index 6330912e6..fd69cad76 100644
--- a/internal/scheduling/lib/filter_weigher_pipeline.go
+++ b/internal/scheduling/lib/filter_weigher_pipeline.go
@@ -19,7 +19,7 @@ import (
type FilterWeigherPipeline[RequestType FilterWeigherPipelineRequest] interface {
// Run the scheduling pipeline with the given request.
- Run(request RequestType) (v1alpha1.DecisionResult, error)
+ Run(ctx context.Context, request RequestType) (v1alpha1.DecisionResult, error)
}
// Pipeline of scheduler steps.
@@ -136,6 +136,7 @@ func InitNewFilterWeigherPipeline[RequestType FilterWeigherPipelineRequest](
// During this process, the request is mutated to only include the
// remaining hosts.
func (p *filterWeigherPipeline[RequestType]) runFilters(
+ ctx context.Context,
log *slog.Logger,
request RequestType,
) (filteredRequest RequestType, stepResults []v1alpha1.StepResult) {
@@ -145,7 +146,7 @@ func (p *filterWeigherPipeline[RequestType]) runFilters(
filter := p.filters[filterName]
stepLog := log.With("filter", filterName)
stepLog.Info("scheduler: running filter")
- result, err := filter.Run(stepLog, filteredRequest)
+ result, err := filter.Run(ctx, stepLog, filteredRequest)
if errors.Is(err, ErrStepSkipped) {
stepLog.Info("scheduler: filter skipped")
continue
@@ -168,6 +169,7 @@ func (p *filterWeigherPipeline[RequestType]) runFilters(
// Execute weighers and collect their activations by step name.
func (p *filterWeigherPipeline[RequestType]) runWeighers(
+ ctx context.Context,
log *slog.Logger,
filteredRequest RequestType,
) map[string]map[string]float64 {
@@ -181,7 +183,7 @@ func (p *filterWeigherPipeline[RequestType]) runWeighers(
wg.Go(func() {
stepLog := log.With("weigher", weigherName)
stepLog.Info("scheduler: running weigher")
- result, err := weigher.Run(stepLog, filteredRequest)
+ result, err := weigher.Run(ctx, stepLog, filteredRequest)
if errors.Is(err, ErrStepSkipped) {
stepLog.Info("scheduler: weigher skipped")
return
@@ -262,7 +264,7 @@ func (s *filterWeigherPipeline[RequestType]) sortHostsByWeights(weights map[stri
}
// Evaluate the pipeline and return a list of hosts in order of preference.
-func (p *filterWeigherPipeline[RequestType]) Run(request RequestType) (v1alpha1.DecisionResult, error) {
+func (p *filterWeigherPipeline[RequestType]) Run(ctx context.Context, request RequestType) (v1alpha1.DecisionResult, error) {
slogArgs := request.GetTraceLogArgs()
slogArgsAny := make([]any, 0, len(slogArgs))
for _, arg := range slogArgs {
@@ -279,7 +281,7 @@ func (p *filterWeigherPipeline[RequestType]) Run(request RequestType) (v1alpha1.
// Run filters first to reduce the number of hosts.
// Any weights assigned to filtered out hosts are ignored.
- filteredRequest, filterStepResults := p.runFilters(traceLog, request)
+ filteredRequest, filterStepResults := p.runFilters(ctx, traceLog, request)
traceLog.Info(
"scheduler: finished filters",
"remainingHosts", filteredRequest.GetHosts(),
@@ -290,7 +292,7 @@ func (p *filterWeigherPipeline[RequestType]) Run(request RequestType) (v1alpha1.
for _, host := range filteredRequest.GetHosts() {
remainingWeights[host] = inWeights[host]
}
- stepWeights := p.runWeighers(traceLog, filteredRequest)
+ stepWeights := p.runWeighers(ctx, traceLog, filteredRequest)
outWeights := p.applyWeights(traceLog, stepWeights, remainingWeights)
traceLog.Info("scheduler: output weights", "weights", outWeights)
diff --git a/internal/scheduling/lib/filter_weigher_pipeline_step.go b/internal/scheduling/lib/filter_weigher_pipeline_step.go
index 26dc5de40..6377335aa 100644
--- a/internal/scheduling/lib/filter_weigher_pipeline_step.go
+++ b/internal/scheduling/lib/filter_weigher_pipeline_step.go
@@ -30,7 +30,7 @@ type FilterWeigherPipelineStep[RequestType FilterWeigherPipelineRequest] interfa
//
// A traceLog is provided that contains the global request id and should
// be used to log the step's execution.
- Run(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error)
+ Run(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error)
}
// Common base for all steps that provides some functionality
diff --git a/internal/scheduling/lib/filter_weigher_pipeline_step_monitor.go b/internal/scheduling/lib/filter_weigher_pipeline_step_monitor.go
index 3e64fa6ee..151a5c180 100644
--- a/internal/scheduling/lib/filter_weigher_pipeline_step_monitor.go
+++ b/internal/scheduling/lib/filter_weigher_pipeline_step_monitor.go
@@ -4,6 +4,7 @@
package lib
import (
+ "context"
"fmt"
"log/slog"
"maps"
@@ -63,6 +64,7 @@ func monitorStep[RequestType FilterWeigherPipelineRequest](stepName string, m Fi
// Run the step and observe its execution.
func (s *FilterWeigherPipelineStepMonitor[RequestType]) RunWrapped(
+ ctx context.Context,
traceLog *slog.Logger,
request RequestType,
step FilterWeigherPipelineStep[RequestType],
@@ -74,7 +76,7 @@ func (s *FilterWeigherPipelineStepMonitor[RequestType]) RunWrapped(
}
inWeights := request.GetWeights()
- stepResult, err := step.Run(traceLog, request)
+ stepResult, err := step.Run(ctx, traceLog, request)
if err != nil {
return nil, err
}
diff --git a/internal/scheduling/lib/filter_weigher_pipeline_step_monitor_test.go b/internal/scheduling/lib/filter_weigher_pipeline_step_monitor_test.go
index 7d7817abd..3357484f7 100644
--- a/internal/scheduling/lib/filter_weigher_pipeline_step_monitor_test.go
+++ b/internal/scheduling/lib/filter_weigher_pipeline_step_monitor_test.go
@@ -4,6 +4,7 @@
package lib
import (
+ "context"
"log/slog"
"os"
"testing"
@@ -28,7 +29,7 @@ func TestStepMonitorRun(t *testing.T) {
removedHostsObserver: removedHostsObserver,
}
step := &mockWeigher[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{"host1": 0.1, "host2": 1.0, "host3": 0.0},
}, nil
@@ -38,7 +39,7 @@ func TestStepMonitorRun(t *testing.T) {
Hosts: []string{"host1", "host2", "host3"},
Weights: map[string]float64{"host1": 0.2, "host2": 0.1, "host3": 0.0},
}
- if _, err := monitor.RunWrapped(slog.Default(), request, step); err != nil {
+ if _, err := monitor.RunWrapped(t.Context(), slog.Default(), request, step); err != nil {
t.Fatalf("Run() error = %v, want nil", err)
}
if len(removedHostsObserver.Observations) != 1 {
diff --git a/internal/scheduling/lib/filter_weigher_pipeline_test.go b/internal/scheduling/lib/filter_weigher_pipeline_test.go
index 0e2775944..6e3a67d49 100644
--- a/internal/scheduling/lib/filter_weigher_pipeline_test.go
+++ b/internal/scheduling/lib/filter_weigher_pipeline_test.go
@@ -25,7 +25,7 @@ func TestPipeline_Run(t *testing.T) {
pipeline := &filterWeigherPipeline[mockFilterWeigherPipelineRequest]{
filters: map[string]Filter[mockFilterWeigherPipelineRequest]{
"mock_filter": &mockFilter[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
// Filter out host3
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{
@@ -39,7 +39,7 @@ func TestPipeline_Run(t *testing.T) {
filtersOrder: []string{"mock_filter"},
weighers: map[string]Weigher[mockFilterWeigherPipelineRequest]{
"mock_weigher": &mockWeigher[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
// Assign weights to hosts
activations := map[string]float64{
"host1": 0.5,
@@ -72,7 +72,7 @@ func TestPipeline_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- result, err := pipeline.Run(tt.request)
+ result, err := pipeline.Run(t.Context(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -197,7 +197,7 @@ func TestPipeline_SortHostsByWeights(t *testing.T) {
func TestPipeline_RunFilters(t *testing.T) {
mockStep := &mockFilter[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
// Filter out host3
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{
@@ -221,7 +221,7 @@ func TestPipeline_RunFilters(t *testing.T) {
Weights: map[string]float64{"host1": 0.0, "host2": 0.0, "host3": 0.0},
}
- req, _ := p.runFilters(slog.Default(), request)
+ req, _ := p.runFilters(t.Context(), slog.Default(), request)
if len(req.Hosts) != 2 {
t.Fatalf("expected 2 step results, got %d", len(req.Hosts))
}
diff --git a/internal/scheduling/lib/weigher_monitor.go b/internal/scheduling/lib/weigher_monitor.go
index df855d067..94f03e615 100644
--- a/internal/scheduling/lib/weigher_monitor.go
+++ b/internal/scheduling/lib/weigher_monitor.go
@@ -43,6 +43,6 @@ func (wm *WeigherMonitor[RequestType]) Validate(ctx context.Context, params v1al
}
// Run the weigher and observe its execution.
-func (wm *WeigherMonitor[RequestType]) Run(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
- return wm.monitor.RunWrapped(traceLog, request, wm.weigher)
+func (wm *WeigherMonitor[RequestType]) Run(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
+ return wm.monitor.RunWrapped(ctx, traceLog, request, wm.weigher)
}
diff --git a/internal/scheduling/lib/weigher_monitor_test.go b/internal/scheduling/lib/weigher_monitor_test.go
index 6f8f906e3..f75077528 100644
--- a/internal/scheduling/lib/weigher_monitor_test.go
+++ b/internal/scheduling/lib/weigher_monitor_test.go
@@ -23,7 +23,7 @@ func TestMonitorWeigher(t *testing.T) {
InitFunc: func(ctx context.Context, cl client.Client, step v1alpha1.WeigherSpec) error {
return nil
},
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{"host1": 0.5, "host2": 1.0},
}, nil
@@ -77,7 +77,7 @@ func TestWeigherMonitor_Init(t *testing.T) {
func TestWeigherMonitor_Run(t *testing.T) {
runCalled := false
mockWeigher := &mockWeigher[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
runCalled = true
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{"host1": 0.5, "host2": 1.0},
@@ -100,7 +100,7 @@ func TestWeigherMonitor_Run(t *testing.T) {
Weights: map[string]float64{"host1": 0.1, "host2": 0.2, "host3": 0.3},
}
- result, err := wm.Run(slog.Default(), request)
+ result, err := wm.Run(t.Context(), slog.Default(), request)
if err != nil {
t.Errorf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/lib/weigher_test.go b/internal/scheduling/lib/weigher_test.go
index 4660207c4..dec8e9378 100644
--- a/internal/scheduling/lib/weigher_test.go
+++ b/internal/scheduling/lib/weigher_test.go
@@ -19,7 +19,7 @@ import (
type mockWeigher[RequestType FilterWeigherPipelineRequest] struct {
InitFunc func(ctx context.Context, client client.Client, step v1alpha1.WeigherSpec) error
ValidateFunc func(ctx context.Context, params v1alpha1.Parameters) error
- RunFunc func(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error)
+ RunFunc func(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error)
}
func (m *mockWeigher[RequestType]) Init(ctx context.Context, client client.Client, step v1alpha1.WeigherSpec) error {
@@ -34,11 +34,11 @@ func (m *mockWeigher[RequestType]) Validate(ctx context.Context, params v1alpha1
}
return m.ValidateFunc(ctx, params)
}
-func (m *mockWeigher[RequestType]) Run(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
+func (m *mockWeigher[RequestType]) Run(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
if m.RunFunc == nil {
return &FilterWeigherPipelineStepResult{}, nil
}
- return m.RunFunc(traceLog, request)
+ return m.RunFunc(ctx, traceLog, request)
}
// weigherTestOptions implements FilterWeigherPipelineStepOpts for testing.
diff --git a/internal/scheduling/lib/weigher_validation.go b/internal/scheduling/lib/weigher_validation.go
index c454d171e..942fc5d42 100644
--- a/internal/scheduling/lib/weigher_validation.go
+++ b/internal/scheduling/lib/weigher_validation.go
@@ -35,8 +35,8 @@ func validateWeigher[RequestType FilterWeigherPipelineRequest](weigher Weigher[R
}
// Run the weigher and validate what happens.
-func (s *WeigherValidator[RequestType]) Run(traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
- result, err := s.Weigher.Run(traceLog, request)
+func (s *WeigherValidator[RequestType]) Run(ctx context.Context, traceLog *slog.Logger, request RequestType) (*FilterWeigherPipelineStepResult, error) {
+ result, err := s.Weigher.Run(ctx, traceLog, request)
if err != nil {
return nil, err
}
diff --git a/internal/scheduling/lib/weigher_validation_test.go b/internal/scheduling/lib/weigher_validation_test.go
index 852448a88..92243d6e8 100644
--- a/internal/scheduling/lib/weigher_validation_test.go
+++ b/internal/scheduling/lib/weigher_validation_test.go
@@ -78,7 +78,7 @@ func TestWeigherValidator_Init(t *testing.T) {
func TestWeigherValidator_Run_ValidHosts(t *testing.T) {
mockStep := &mockWeigher[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{
"host1": 1.0,
@@ -96,7 +96,7 @@ func TestWeigherValidator_Run_ValidHosts(t *testing.T) {
Weigher: mockStep,
}
- result, err := validator.Run(slog.Default(), request)
+ result, err := validator.Run(t.Context(), slog.Default(), request)
if err != nil {
t.Errorf("Run() error = %v, want nil", err)
}
@@ -113,7 +113,7 @@ func TestWeigherValidator_Run_ValidHosts(t *testing.T) {
func TestWeigherValidator_Run_HostNumberMismatch(t *testing.T) {
mockStep := &mockWeigher[mockFilterWeigherPipelineRequest]{
- RunFunc: func(traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
+ RunFunc: func(_ context.Context, traceLog *slog.Logger, request mockFilterWeigherPipelineRequest) (*FilterWeigherPipelineStepResult, error) {
return &FilterWeigherPipelineStepResult{
Activations: map[string]float64{
"host1": 1.0,
@@ -130,7 +130,7 @@ func TestWeigherValidator_Run_HostNumberMismatch(t *testing.T) {
Weigher: mockStep,
}
- result, err := validator.Run(slog.Default(), request)
+ result, err := validator.Run(t.Context(), slog.Default(), request)
if err == nil {
t.Errorf("Run() error = nil, want error")
}
diff --git a/internal/scheduling/machines/filter_weigher_pipeline_controller.go b/internal/scheduling/machines/filter_weigher_pipeline_controller.go
index 35d51708a..98be48d8f 100644
--- a/internal/scheduling/machines/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/machines/filter_weigher_pipeline_controller.go
@@ -144,7 +144,7 @@ func (c *FilterWeigherPipelineController) process(ctx context.Context, decision
// Execute the scheduling pipeline.
request := ironcore.MachinePipelineRequest{Pools: pools.Items}
- result, err := pipeline.Run(request)
+ result, err := pipeline.Run(ctx, request)
if err != nil {
log.V(1).Error(err, "failed to run scheduler pipeline")
return errors.New("failed to run scheduler pipeline")
diff --git a/internal/scheduling/machines/filter_weigher_pipeline_controller_test.go b/internal/scheduling/machines/filter_weigher_pipeline_controller_test.go
index bc2e0722a..763b0bab7 100644
--- a/internal/scheduling/machines/filter_weigher_pipeline_controller_test.go
+++ b/internal/scheduling/machines/filter_weigher_pipeline_controller_test.go
@@ -516,7 +516,7 @@ func createMockPipeline() lib.FilterWeigherPipeline[ironcore.MachinePipelineRequ
type mockMachinePipeline struct{}
-func (m *mockMachinePipeline) Run(request ironcore.MachinePipelineRequest) (v1alpha1.DecisionResult, error) {
+func (m *mockMachinePipeline) Run(_ context.Context, request ironcore.MachinePipelineRequest) (v1alpha1.DecisionResult, error) {
if len(request.Pools) == 0 {
return v1alpha1.DecisionResult{}, nil
}
diff --git a/internal/scheduling/machines/plugins/filters/filter_noop.go b/internal/scheduling/machines/plugins/filters/filter_noop.go
index da901e5c0..55015ddcd 100644
--- a/internal/scheduling/machines/plugins/filters/filter_noop.go
+++ b/internal/scheduling/machines/plugins/filters/filter_noop.go
@@ -31,7 +31,7 @@ func (f *NoopFilter) Validate(ctx context.Context, params v1alpha1.Parameters) e
// not in the map are considered as filtered out.
// Provide a traceLog that contains the global request id and should
// be used to log the step's execution.
-func (NoopFilter) Run(traceLog *slog.Logger, request ironcore.MachinePipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (NoopFilter) Run(_ context.Context, traceLog *slog.Logger, request ironcore.MachinePipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
activations := make(map[string]float64, len(request.Pools))
stats := make(map[string]lib.FilterWeigherPipelineStepStatistics)
// Usually you would do some filtering here, or adjust the weights.
diff --git a/internal/scheduling/machines/plugins/filters/filter_noop_test.go b/internal/scheduling/machines/plugins/filters/filter_noop_test.go
index 2fa369a4f..759ab5ee2 100644
--- a/internal/scheduling/machines/plugins/filters/filter_noop_test.go
+++ b/internal/scheduling/machines/plugins/filters/filter_noop_test.go
@@ -64,7 +64,7 @@ func TestNoopFilter_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter := &NoopFilter{}
- result, err := filter.Run(slog.Default(), tt.request)
+ result, err := filter.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Errorf("expected Run() to succeed, got error: %v", err)
diff --git a/internal/scheduling/manila/filter_weigher_pipeline_controller.go b/internal/scheduling/manila/filter_weigher_pipeline_controller.go
index 128b7d719..41931f154 100644
--- a/internal/scheduling/manila/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/manila/filter_weigher_pipeline_controller.go
@@ -121,7 +121,7 @@ func (c *FilterWeigherPipelineController) process(ctx context.Context, decision
return err
}
- result, err := pipeline.Run(request)
+ result, err := pipeline.Run(ctx, request)
if err != nil {
log.Error(err, "failed to run pipeline")
return err
diff --git a/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go b/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go
index ce3e30ebe..44eaba0e0 100644
--- a/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go
+++ b/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go
@@ -61,14 +61,14 @@ func (s *NetappCPUUsageBalancingStep) Init(ctx context.Context, client client.Cl
}
// Downvote hosts that are highly contended.
-func (s *NetappCPUUsageBalancingStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *NetappCPUUsageBalancingStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["avg cpu contention"] = s.PrepareStats(request, "%")
result.Statistics["max cpu contention"] = s.PrepareStats(request, "%")
knowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "netapp-storage-pool-cpu-usage-manila"},
knowledge,
); err != nil {
diff --git a/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing_test.go b/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing_test.go
index f3e9c66ea..dfa87bf40 100644
--- a/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing_test.go
+++ b/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing_test.go
@@ -164,7 +164,7 @@ func TestNetappCPUUsageBalancingStep_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/filter_weigher_pipeline_controller.go b/internal/scheduling/nova/filter_weigher_pipeline_controller.go
index 279ac1c3e..dea97eb40 100644
--- a/internal/scheduling/nova/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/nova/filter_weigher_pipeline_controller.go
@@ -166,7 +166,7 @@ func (c *FilterWeigherPipelineController) process(ctx context.Context, decision
log.Info("gathered all placement candidates", "numHosts", len(request.Hosts))
}
- result, err := pipeline.Run(request)
+ result, err := pipeline.Run(ctx, request)
if err != nil {
log.Error(err, "failed to run pipeline")
return err
diff --git a/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go b/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go
index 157a80521..dd645020e 100644
--- a/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go
+++ b/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go
@@ -19,11 +19,11 @@ type FilterAggregateMetadata struct {
// Restrict hosts to specific projects if they are in an aggregate that has
// the "filter_tenant_id" metadata key set.
-func (s *FilterAggregateMetadata) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterAggregateMetadata) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata_test.go b/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata_test.go
index d1ff9cd2d..9463f4345 100644
--- a/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata_test.go
@@ -336,7 +336,7 @@ func TestFilterAggregateMetadata_Run(t *testing.T) {
step := &FilterAggregateMetadata{}
step.Client = fakeClient
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -388,7 +388,7 @@ func TestFilterAggregateMetadata_Run_ClientError(t *testing.T) {
step := &FilterAggregateMetadata{}
step.Client = fakeClient
- _, err := step.Run(slog.Default(), request)
+ _, err := step.Run(t.Context(), slog.Default(), request)
if err == nil {
t.Errorf("expected error when client fails, got none")
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go b/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go
index a0a486f3d..850224019 100644
--- a/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go
+++ b/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go
@@ -19,7 +19,7 @@ type FilterAllowedProjectsStep struct {
// Lock certain hosts for certain projects, based on the hypervisor spec.
// Note that hosts without specified projects are still accessible.
-func (s *FilterAllowedProjectsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterAllowedProjectsStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
if request.Spec.Data.ProjectID == "" {
traceLog.Info("no project ID in request, skipping filter")
@@ -27,7 +27,7 @@ func (s *FilterAllowedProjectsStep) Run(traceLog *slog.Logger, request api.Exter
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_allowed_projects_test.go b/internal/scheduling/nova/plugins/filters/filter_allowed_projects_test.go
index 070160e2e..0965a0f96 100644
--- a/internal/scheduling/nova/plugins/filters/filter_allowed_projects_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_allowed_projects_test.go
@@ -295,7 +295,7 @@ func TestFilterAllowedProjectsStep_Run(t *testing.T) {
WithScheme(scheme).
WithObjects(hvs...).
Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_capabilities.go b/internal/scheduling/nova/plugins/filters/filter_capabilities.go
index 8cf6afac9..d0d07d83a 100644
--- a/internal/scheduling/nova/plugins/filters/filter_capabilities.go
+++ b/internal/scheduling/nova/plugins/filters/filter_capabilities.go
@@ -45,7 +45,7 @@ func hvToNovaCapabilities(hv hv1.Hypervisor) (map[string]string, error) {
// Check the capabilities of each host and if they match the extra spec provided
// in the request spec flavor.
-func (s *FilterCapabilitiesStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterCapabilitiesStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
extraSpecs := request.Spec.Data.Flavor.Data.ExtraSpecs
if len(extraSpecs) == 0 {
@@ -86,7 +86,7 @@ func (s *FilterCapabilitiesStep) Run(traceLog *slog.Logger, request api.External
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go b/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go
index 9b5f111dc..bf3b39334 100644
--- a/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go
@@ -553,7 +553,7 @@ func TestFilterCapabilitiesStep_Run(t *testing.T) {
WithScheme(scheme).
WithObjects(hvs...).
Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -628,7 +628,7 @@ func TestFilterCapabilitiesStep_DoesNotMutateExtraSpecs(t *testing.T) {
WithObjects(hvs...).
Build()
- _, err = step.Run(slog.Default(), request)
+ _, err = step.Run(t.Context(), slog.Default(), request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_correct_az.go b/internal/scheduling/nova/plugins/filters/filter_correct_az.go
index ed7f68188..5cc40bf86 100644
--- a/internal/scheduling/nova/plugins/filters/filter_correct_az.go
+++ b/internal/scheduling/nova/plugins/filters/filter_correct_az.go
@@ -18,7 +18,7 @@ type FilterCorrectAZStep struct {
}
// Only get hosts in the requested az.
-func (s *FilterCorrectAZStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterCorrectAZStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
if request.Spec.Data.AvailabilityZone == "" {
traceLog.Info("no availability zone requested, skipping filter_correct_az step")
@@ -26,7 +26,7 @@ func (s *FilterCorrectAZStep) Run(traceLog *slog.Logger, request api.ExternalSch
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_correct_az_test.go b/internal/scheduling/nova/plugins/filters/filter_correct_az_test.go
index d8389de9e..84844f082 100644
--- a/internal/scheduling/nova/plugins/filters/filter_correct_az_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_correct_az_test.go
@@ -169,7 +169,7 @@ func TestFilterCorrectAZStep_Run(t *testing.T) {
WithScheme(scheme).
WithObjects(hvs...).
Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_exclude_hosts.go b/internal/scheduling/nova/plugins/filters/filter_exclude_hosts.go
index 231efa9aa..6ed33543f 100644
--- a/internal/scheduling/nova/plugins/filters/filter_exclude_hosts.go
+++ b/internal/scheduling/nova/plugins/filters/filter_exclude_hosts.go
@@ -4,6 +4,7 @@
package filters
import (
+ "context"
"log/slog"
api "github.com/cobaltcore-dev/cortex/api/external/nova"
@@ -28,6 +29,7 @@ type FilterExcludeHostsStepOpts struct {
func (opts FilterExcludeHostsStepOpts) Validate() error { return nil }
func (s *FilterExcludeHostsStep) Run(
+ _ context.Context,
traceLog *slog.Logger,
request api.ExternalSchedulerRequest,
) (*lib.FilterWeigherPipelineStepResult, error) {
diff --git a/internal/scheduling/nova/plugins/filters/filter_exclude_hosts_test.go b/internal/scheduling/nova/plugins/filters/filter_exclude_hosts_test.go
index 0c9e35c59..33e34ef92 100644
--- a/internal/scheduling/nova/plugins/filters/filter_exclude_hosts_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_exclude_hosts_test.go
@@ -218,7 +218,7 @@ func TestFilterExcludeHostsStep_Run(t *testing.T) {
ExcludedHosts: tt.excludedHosts,
}
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_external_customer.go b/internal/scheduling/nova/plugins/filters/filter_external_customer.go
index 56f73c8ac..8df79e4e0 100644
--- a/internal/scheduling/nova/plugins/filters/filter_external_customer.go
+++ b/internal/scheduling/nova/plugins/filters/filter_external_customer.go
@@ -33,7 +33,7 @@ type FilterExternalCustomerStep struct {
// Prefix-match the domain name for external customer domains and filter out hosts
// that are not intended for external customers.
-func (s *FilterExternalCustomerStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterExternalCustomerStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
domainName, err := request.Spec.Data.GetSchedulerHintStr("domain_name")
if err != nil {
@@ -57,7 +57,7 @@ func (s *FilterExternalCustomerStep) Run(traceLog *slog.Logger, request api.Exte
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_external_customer_test.go b/internal/scheduling/nova/plugins/filters/filter_external_customer_test.go
index 7ca313dc3..a715bcfd6 100644
--- a/internal/scheduling/nova/plugins/filters/filter_external_customer_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_external_customer_test.go
@@ -358,7 +358,7 @@ func TestFilterExternalCustomerStep_Run(t *testing.T) {
Build()
step.Options = tt.opts
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if tt.expectError {
if err == nil {
t.Errorf("expected error but got none")
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go b/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go
index dcccdc010..3924e152c 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go
@@ -18,7 +18,7 @@ type FilterHasAcceleratorsStep struct {
}
// If requested, only get hosts with accelerators.
-func (s *FilterHasAcceleratorsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterHasAcceleratorsStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
extraSpecs := request.Spec.Data.Flavor.Data.ExtraSpecs
if _, ok := extraSpecs["accel:device_profile"]; !ok {
@@ -27,7 +27,7 @@ func (s *FilterHasAcceleratorsStep) Run(traceLog *slog.Logger, request api.Exter
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_accelerators_test.go b/internal/scheduling/nova/plugins/filters/filter_has_accelerators_test.go
index 1d1a06764..a1d5c2355 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_accelerators_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_accelerators_test.go
@@ -347,7 +347,7 @@ func TestFilterHasAcceleratorsStep_Run(t *testing.T) {
WithObjects(hvs...).
Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
index d26c7c940..c8e7b88a1 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
@@ -48,7 +48,7 @@ type FilterHasEnoughCapacity struct {
// known at this point.
//
// Please also note that disk space is currently not considered by this filter.
-func (s *FilterHasEnoughCapacity) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterHasEnoughCapacity) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
// This map holds the free resources per host.
@@ -58,7 +58,7 @@ func (s *FilterHasEnoughCapacity) Run(traceLog *slog.Logger, request api.Externa
// We can use the hypervisor status to calculate the total capacity
// and then subtract the actual resource allocation from virtual machines.
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
@@ -88,7 +88,7 @@ func (s *FilterHasEnoughCapacity) Run(traceLog *slog.Logger, request api.Externa
// Subtract reserved resources by Reservations.
var reservations v1alpha1.ReservationList
- if err := s.Client.List(context.Background(), &reservations); err != nil {
+ if err := s.Client.List(ctx, &reservations); err != nil {
return nil, err
}
for _, reservation := range reservations.Items {
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go
index cabc3a3b4..9c52bccd3 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go
@@ -503,7 +503,7 @@ func TestFilterHasEnoughCapacity_ReservationTypes(t *testing.T) {
step.Client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
step.Options = tt.opts
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -792,7 +792,7 @@ func TestFilterHasEnoughCapacity_IgnoredReservationTypes(t *testing.T) {
IgnoredReservationTypes: tt.ignoredReservationTypes,
}
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -966,7 +966,7 @@ func TestFilterHasEnoughCapacity_ReserveForCommittedResourceIntent(t *testing.T)
step.Client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
step.Options = tt.opts
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -1050,7 +1050,7 @@ func TestFilterHasEnoughCapacity_NilEffectiveCapacityFallback(t *testing.T) {
step.Client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
step.Options = FilterHasEnoughCapacityOpts{LockReserved: false}
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go b/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go
index aa35d2fc9..308cfc4d3 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go
@@ -21,7 +21,7 @@ type FilterHasRequestedTraits struct {
// Filter hosts that do not have the requested traits given by the extra spec:
// - "trait:": "forbidden" means the host must not have the specified trait.
// - "trait:": "required" means the host must have the specified trait.
-func (s *FilterHasRequestedTraits) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterHasRequestedTraits) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
var requiredTraits, forbiddenTraits []string
for key, value := range request.Spec.Data.Flavor.Data.ExtraSpecs {
@@ -55,7 +55,7 @@ func (s *FilterHasRequestedTraits) Run(traceLog *slog.Logger, request api.Extern
)
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_requested_traits_test.go b/internal/scheduling/nova/plugins/filters/filter_has_requested_traits_test.go
index 10a7c94aa..8e917e23d 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_requested_traits_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_requested_traits_test.go
@@ -461,7 +461,7 @@ func TestFilterHasRequestedTraits_Run(t *testing.T) {
WithObjects(hvs...).
Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_host_instructions.go b/internal/scheduling/nova/plugins/filters/filter_host_instructions.go
index dafb6675f..1b5d8f1cf 100644
--- a/internal/scheduling/nova/plugins/filters/filter_host_instructions.go
+++ b/internal/scheduling/nova/plugins/filters/filter_host_instructions.go
@@ -4,6 +4,7 @@
package filters
import (
+ "context"
"log/slog"
"slices"
@@ -18,7 +19,7 @@ type FilterHostInstructionsStep struct {
// Filter hosts based on instructions given in the request spec. Supported are:
// - spec.ignore_hosts: Filter out all hosts in this list.
// - spec.force_hosts: Include only hosts in this list.
-func (s *FilterHostInstructionsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterHostInstructionsStep) Run(_ context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
if request.Spec.Data.IgnoreHosts != nil {
for _, host := range *request.Spec.Data.IgnoreHosts {
diff --git a/internal/scheduling/nova/plugins/filters/filter_host_instructions_test.go b/internal/scheduling/nova/plugins/filters/filter_host_instructions_test.go
index 10bcb60c9..8af9b50c3 100644
--- a/internal/scheduling/nova/plugins/filters/filter_host_instructions_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_host_instructions_test.go
@@ -331,7 +331,7 @@ func TestFilterHostInstructionsStep_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
step := &FilterHostInstructionsStep{}
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity.go b/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity.go
index 326864b9d..569e9eca0 100644
--- a/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity.go
+++ b/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity.go
@@ -4,6 +4,7 @@
package filters
import (
+ "context"
"log/slog"
"slices"
@@ -17,6 +18,7 @@ type FilterInstanceGroupAffinityStep struct {
// Select hosts in spec.instance_group.
func (s *FilterInstanceGroupAffinityStep) Run(
+ _ context.Context,
traceLog *slog.Logger,
request api.ExternalSchedulerRequest,
) (*lib.FilterWeigherPipelineStepResult, error) {
diff --git a/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity_test.go b/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity_test.go
index 7321747e3..89465ed46 100644
--- a/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_instance_group_affinity_test.go
@@ -326,7 +326,7 @@ func TestFilterInstanceGroupAffinityStep_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
step := &FilterInstanceGroupAffinityStep{}
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go b/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go
index 0dee29d9e..1fc69ffd3 100644
--- a/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go
+++ b/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go
@@ -20,6 +20,7 @@ type FilterInstanceGroupAntiAffinityStep struct {
// Select hosts not in spec_obj.instance_group but only until
// max_server_per_host is reached (by default = 1).
func (s *FilterInstanceGroupAntiAffinityStep) Run(
+ ctx context.Context,
traceLog *slog.Logger,
request api.ExternalSchedulerRequest,
) (*lib.FilterWeigherPipelineStepResult, error) {
@@ -52,7 +53,7 @@ func (s *FilterInstanceGroupAntiAffinityStep) Run(
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity_test.go b/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity_test.go
index 6eea6bc7f..e56486871 100644
--- a/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity_test.go
@@ -519,7 +519,7 @@ func TestFilterInstanceGroupAntiAffinityStep_Run(t *testing.T) {
WithScheme(scheme).
WithObjects(hvs...).
Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_live_migratable.go b/internal/scheduling/nova/plugins/filters/filter_live_migratable.go
index a19238721..97017039b 100644
--- a/internal/scheduling/nova/plugins/filters/filter_live_migratable.go
+++ b/internal/scheduling/nova/plugins/filters/filter_live_migratable.go
@@ -49,6 +49,7 @@ func (s *FilterLiveMigratableStep) checkHasSufficientFeatures(
// Ensure the target host of a live migration can accept the migrating VM.
func (s *FilterLiveMigratableStep) Run(
+ ctx context.Context,
traceLog *slog.Logger,
request api.ExternalSchedulerRequest,
) (*lib.FilterWeigherPipelineStepResult, error) {
@@ -73,7 +74,7 @@ func (s *FilterLiveMigratableStep) Run(
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_live_migratable_test.go b/internal/scheduling/nova/plugins/filters/filter_live_migratable_test.go
index c5651b025..5af0d8f55 100644
--- a/internal/scheduling/nova/plugins/filters/filter_live_migratable_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_live_migratable_test.go
@@ -641,7 +641,7 @@ func TestFilterLiveMigratableStep_Run(t *testing.T) {
},
}
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if tt.expectErr {
if err == nil {
@@ -728,7 +728,7 @@ func TestFilterLiveMigratableStep_Run_SourceHostNotFound(t *testing.T) {
},
}
- _, err := step.Run(slog.Default(), request)
+ _, err := step.Run(t.Context(), slog.Default(), request)
if err == nil {
t.Errorf("expected error when source host not found, got none")
}
@@ -774,7 +774,7 @@ func TestFilterLiveMigratableStep_Run_ClientError(t *testing.T) {
},
}
- _, err := step.Run(slog.Default(), request)
+ _, err := step.Run(t.Context(), slog.Default(), request)
if err == nil {
t.Errorf("expected error when client fails, got none")
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_requested_destination.go b/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
index 8922ab8c4..56b3a162a 100644
--- a/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
+++ b/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
@@ -100,7 +100,7 @@ func (s *FilterRequestedDestinationStep) processRequestedHost(
// The requested destination can include a specific host, aggregates, or both.
// When both are specified, aggregate filtering is applied first, followed by
// host filtering.
-func (s *FilterRequestedDestinationStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterRequestedDestinationStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
rd := request.Spec.Data.RequestedDestination
if rd == nil {
@@ -112,7 +112,7 @@ func (s *FilterRequestedDestinationStep) Run(traceLog *slog.Logger, request api.
return result, nil
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_requested_destination_test.go b/internal/scheduling/nova/plugins/filters/filter_requested_destination_test.go
index 5a752160e..c3e473780 100644
--- a/internal/scheduling/nova/plugins/filters/filter_requested_destination_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_requested_destination_test.go
@@ -578,7 +578,7 @@ func TestFilterRequestedDestinationStep_Run(t *testing.T) {
},
}
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if tt.expectErr {
if err == nil {
@@ -777,7 +777,7 @@ func TestFilterRequestedDestinationStep_Run_ClientError(t *testing.T) {
},
}
- _, err := step.Run(slog.Default(), request)
+ _, err := step.Run(t.Context(), slog.Default(), request)
if err == nil {
t.Errorf("expected error when client fails, got none")
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_status_conditions.go b/internal/scheduling/nova/plugins/filters/filter_status_conditions.go
index 3d7f2aae6..f45df0af2 100644
--- a/internal/scheduling/nova/plugins/filters/filter_status_conditions.go
+++ b/internal/scheduling/nova/plugins/filters/filter_status_conditions.go
@@ -20,11 +20,11 @@ type FilterStatusConditionsStep struct {
// Check that all status conditions meet the expected values, for example,
// that the hypervisor is ready and not disabled.
-func (s *FilterStatusConditionsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterStatusConditionsStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_status_conditions_test.go b/internal/scheduling/nova/plugins/filters/filter_status_conditions_test.go
index adbfc8c65..453ebe91e 100644
--- a/internal/scheduling/nova/plugins/filters/filter_status_conditions_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_status_conditions_test.go
@@ -341,7 +341,7 @@ func TestFilterStatusConditionsStep_Run(t *testing.T) {
WithScheme(scheme).
WithObjects(hvs...).
Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_binpack.go b/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
index e1509a4cc..6331fd4e9 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
@@ -69,12 +69,12 @@ type KVMBinpackStep struct {
}
// Run this weigher in the pipeline after filters have been executed.
-func (s *KVMBinpackStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *KVMBinpackStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["binpack score"] = s.PrepareStats(request, "float")
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go b/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go
index 69e1aa9f6..80faf6154 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go
@@ -448,7 +448,7 @@ func TestKVMBinpackStep_Run(t *testing.T) {
step.Client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
step.Options = tt.opts
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error, got nil")
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation.go b/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation.go
index 1f404f6b5..1437a515c 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation.go
@@ -51,7 +51,7 @@ type KVMFailoverEvacuationStep struct {
// Run the weigher step.
// For evacuation requests, hosts matching a failover reservation where the VM is in Allocations get a higher weight.
// For non-evacuation requests (e.g., live migration, rebuild), this weigher has no effect.
-func (s *KVMFailoverEvacuationStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *KVMFailoverEvacuationStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
intent, err := request.GetIntent()
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation_test.go b/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation_test.go
index 0664e55d4..475f363ab 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation_test.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_failover_evacuation_test.go
@@ -264,7 +264,7 @@ func TestKVMFailoverEvacuationStep_Run(t *testing.T) {
step.Client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
step.Options = tt.opts
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go b/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go
index 5f13897f0..bc4db67ca 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go
@@ -26,7 +26,7 @@ type KVMInstanceGroupSoftAffinityStep struct {
lib.BaseWeigher[api.ExternalSchedulerRequest, lib.EmptyFilterWeigherPipelineStepOpts]
}
-func (s *KVMInstanceGroupSoftAffinityStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *KVMInstanceGroupSoftAffinityStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["affinity"] = s.PrepareStats(request, "float")
@@ -52,7 +52,7 @@ func (s *KVMInstanceGroupSoftAffinityStep) Run(traceLog *slog.Logger, request ap
}
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity_test.go b/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity_test.go
index fcf13f86b..77be8b610 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity_test.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity_test.go
@@ -346,7 +346,7 @@ func TestKVMInstanceGroupSoftAffinityStep_Run(t *testing.T) {
step := &KVMInstanceGroupSoftAffinityStep{}
step.Client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error, got nil")
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go
index b65a5f75f..1fb08039f 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go
@@ -57,12 +57,12 @@ type KVMPreferSmallerHostsStep struct {
}
// Run this weigher in the pipeline after filters have been executed.
-func (s *KVMPreferSmallerHostsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *KVMPreferSmallerHostsStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["small host score"] = s.PrepareStats(request, "float")
hvs := &hv1.HypervisorList{}
- if err := s.Client.List(context.Background(), hvs); err != nil {
+ if err := s.Client.List(ctx, hvs); err != nil {
traceLog.Error("failed to list hypervisors", "error", err)
return nil, err
}
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go
index 2ab2deb89..b8be81784 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go
@@ -599,7 +599,7 @@ func TestKVMPreferSmallerHostsStep_Run(t *testing.T) {
step.Client = fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
step.Options = tt.opts
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error, got nil")
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go b/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go
index b2b886d49..5cbdf69e2 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go
@@ -52,14 +52,14 @@ func (s *VMwareAntiAffinityNoisyProjectsStep) Init(ctx context.Context, client c
}
// Downvote the hosts a project is currently running on if it's noisy.
-func (s *VMwareAntiAffinityNoisyProjectsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *VMwareAntiAffinityNoisyProjectsStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["avg cpu usage of this project"] = s.PrepareStats(request, "%")
knowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "vmware-project-noisiness"},
knowledge,
); err != nil {
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects_test.go b/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects_test.go
index 304ab0612..348e111eb 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects_test.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects_test.go
@@ -273,7 +273,7 @@ func TestVMwareAntiAffinityNoisyProjectsStep_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go b/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go
index 14905cf00..a97ca8fe9 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go
@@ -61,7 +61,7 @@ func (s *VMwareAvoidLongTermContendedHostsStep) Init(ctx context.Context, client
}
// Downvote hosts that are highly contended.
-func (s *VMwareAvoidLongTermContendedHostsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *VMwareAvoidLongTermContendedHostsStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["avg cpu contention"] = s.PrepareStats(request, "%")
@@ -69,7 +69,7 @@ func (s *VMwareAvoidLongTermContendedHostsStep) Run(traceLog *slog.Logger, reque
knowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "vmware-long-term-contended-hosts"},
knowledge,
); err != nil {
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts_test.go b/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts_test.go
index 5a69cdaf7..73c9d3454 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts_test.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts_test.go
@@ -256,7 +256,7 @@ func TestVMwareAvoidLongTermContendedHostsStep_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go b/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go
index fd9e81335..3f93bc158 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go
@@ -61,7 +61,7 @@ func (s *VMwareAvoidShortTermContendedHostsStep) Init(ctx context.Context, clien
}
// Downvote hosts that are highly contended.
-func (s *VMwareAvoidShortTermContendedHostsStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *VMwareAvoidShortTermContendedHostsStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["avg cpu contention"] = s.PrepareStats(request, "%")
@@ -69,7 +69,7 @@ func (s *VMwareAvoidShortTermContendedHostsStep) Run(traceLog *slog.Logger, requ
knowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "vmware-short-term-contended-hosts"},
knowledge,
); err != nil {
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts_test.go b/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts_test.go
index 0dfe280d0..35b0d86c6 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts_test.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts_test.go
@@ -256,7 +256,7 @@ func TestVMwareAvoidShortTermContendedHostsStep_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_binpack.go b/internal/scheduling/nova/plugins/weighers/vmware_binpack.go
index 217dc7d7f..bdcb46339 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_binpack.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_binpack.go
@@ -85,13 +85,13 @@ func (s *VMwareBinpackStep) Init(ctx context.Context, client client.Client, weig
}
// Run this weigher in the pipeline after filters have been executed.
-func (s *VMwareBinpackStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *VMwareBinpackStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
result.Statistics["binpack score"] = s.PrepareStats(request, "float")
hostUtilizationKnowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "host-utilization"},
hostUtilizationKnowledge,
); err != nil {
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_binpack_test.go b/internal/scheduling/nova/plugins/weighers/vmware_binpack_test.go
index cdca6c569..2c1d3a95c 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_binpack_test.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_binpack_test.go
@@ -211,7 +211,7 @@ func TestVMwareBinpackStep_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- result, err := step.Run(slog.Default(), tt.request)
+ result, err := step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/scheduling/pods/filter_weigher_pipeline_controller.go b/internal/scheduling/pods/filter_weigher_pipeline_controller.go
index 0ceee6485..b12704b50 100644
--- a/internal/scheduling/pods/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/pods/filter_weigher_pipeline_controller.go
@@ -158,7 +158,7 @@ func (c *FilterWeigherPipelineController) process(ctx context.Context, decision
// Execute the scheduling pipeline.
request := pods.PodPipelineRequest{Nodes: nodes.Items, Pod: *pod}
- result, err := pipeline.Run(request)
+ result, err := pipeline.Run(ctx, request)
if err != nil {
log.V(1).Error(err, "failed to run scheduler pipeline")
return errors.New("failed to run scheduler pipeline")
diff --git a/internal/scheduling/pods/filter_weigher_pipeline_controller_test.go b/internal/scheduling/pods/filter_weigher_pipeline_controller_test.go
index 143ed9f83..d93edee58 100644
--- a/internal/scheduling/pods/filter_weigher_pipeline_controller_test.go
+++ b/internal/scheduling/pods/filter_weigher_pipeline_controller_test.go
@@ -492,7 +492,7 @@ func createMockPodPipeline() lib.FilterWeigherPipeline[pods.PodPipelineRequest]
type mockPodPipeline struct{}
-func (m *mockPodPipeline) Run(request pods.PodPipelineRequest) (v1alpha1.DecisionResult, error) {
+func (m *mockPodPipeline) Run(_ context.Context, request pods.PodPipelineRequest) (v1alpha1.DecisionResult, error) {
if len(request.Nodes) == 0 {
return v1alpha1.DecisionResult{}, nil
}
diff --git a/internal/scheduling/pods/plugins/filters/filter_node_affinity.go b/internal/scheduling/pods/plugins/filters/filter_node_affinity.go
index 996897bdb..3799a7a71 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_affinity.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_affinity.go
@@ -27,7 +27,7 @@ func (f *NodeAffinityFilter) Validate(ctx context.Context, params v1alpha1.Param
return nil
}
-func (NodeAffinityFilter) Run(traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (NodeAffinityFilter) Run(_ context.Context, traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
activations := make(map[string]float64)
stats := make(map[string]lib.FilterWeigherPipelineStepStatistics)
diff --git a/internal/scheduling/pods/plugins/filters/filter_node_affinity_test.go b/internal/scheduling/pods/plugins/filters/filter_node_affinity_test.go
index 0172d60d7..8e766a925 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_affinity_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_affinity_test.go
@@ -399,7 +399,7 @@ func TestNodeAffinityFilter_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter := &NodeAffinityFilter{}
- result, err := filter.Run(slog.Default(), tt.request)
+ result, err := filter.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Errorf("expected Run() to succeed, got error: %v", err)
diff --git a/internal/scheduling/pods/plugins/filters/filter_node_available.go b/internal/scheduling/pods/plugins/filters/filter_node_available.go
index 2d6e11d22..13342ebfc 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_available.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_available.go
@@ -26,7 +26,7 @@ func (f *NodeAvailableFilter) Validate(ctx context.Context, params v1alpha1.Para
return nil
}
-func (NodeAvailableFilter) Run(traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (NodeAvailableFilter) Run(_ context.Context, traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
activations := make(map[string]float64)
stats := make(map[string]lib.FilterWeigherPipelineStepStatistics)
diff --git a/internal/scheduling/pods/plugins/filters/filter_node_available_test.go b/internal/scheduling/pods/plugins/filters/filter_node_available_test.go
index 3eac7873c..1286ef8a1 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_available_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_available_test.go
@@ -309,7 +309,7 @@ func TestNodeAvailableFilter_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter := &NodeAvailableFilter{}
- result, err := filter.Run(slog.Default(), tt.request)
+ result, err := filter.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Errorf("expected Run() to succeed, got error: %v", err)
diff --git a/internal/scheduling/pods/plugins/filters/filter_node_capacity.go b/internal/scheduling/pods/plugins/filters/filter_node_capacity.go
index cfceb8835..305a33427 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_capacity.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_capacity.go
@@ -27,7 +27,7 @@ func (f *NodeCapacityFilter) Validate(ctx context.Context, params v1alpha1.Param
return nil
}
-func (NodeCapacityFilter) Run(traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (NodeCapacityFilter) Run(_ context.Context, traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
activations := make(map[string]float64)
stats := make(map[string]lib.FilterWeigherPipelineStepStatistics)
diff --git a/internal/scheduling/pods/plugins/filters/filter_node_capacity_test.go b/internal/scheduling/pods/plugins/filters/filter_node_capacity_test.go
index 543b4561d..0196a2158 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_capacity_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_capacity_test.go
@@ -351,7 +351,7 @@ func TestNodeCapacityFilter_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter := &NodeCapacityFilter{}
- result, err := filter.Run(slog.Default(), tt.request)
+ result, err := filter.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Errorf("expected Run() to succeed, got error: %v", err)
diff --git a/internal/scheduling/pods/plugins/filters/filter_noop.go b/internal/scheduling/pods/plugins/filters/filter_noop.go
index e0666537b..0a1e80d9d 100644
--- a/internal/scheduling/pods/plugins/filters/filter_noop.go
+++ b/internal/scheduling/pods/plugins/filters/filter_noop.go
@@ -31,7 +31,7 @@ func (f *NoopFilter) Validate(ctx context.Context, params v1alpha1.Parameters) e
// not in the map are considered as filtered out.
// Provide a traceLog that contains the global request id and should
// be used to log the step's execution.
-func (NoopFilter) Run(traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (NoopFilter) Run(_ context.Context, traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
activations := make(map[string]float64, len(request.Nodes))
stats := make(map[string]lib.FilterWeigherPipelineStepStatistics)
// Usually you would do some filtering here, or adjust the weights.
diff --git a/internal/scheduling/pods/plugins/filters/filter_noop_test.go b/internal/scheduling/pods/plugins/filters/filter_noop_test.go
index dcdd90a69..2440f39af 100644
--- a/internal/scheduling/pods/plugins/filters/filter_noop_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_noop_test.go
@@ -85,7 +85,7 @@ func TestNoopFilter_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter := &NoopFilter{}
- result, err := filter.Run(slog.Default(), tt.request)
+ result, err := filter.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Errorf("expected Run() to succeed, got error: %v", err)
diff --git a/internal/scheduling/pods/plugins/filters/filter_taint.go b/internal/scheduling/pods/plugins/filters/filter_taint.go
index 5a54d1cb6..c51b0fb6d 100644
--- a/internal/scheduling/pods/plugins/filters/filter_taint.go
+++ b/internal/scheduling/pods/plugins/filters/filter_taint.go
@@ -26,7 +26,7 @@ func (f *TaintFilter) Validate(ctx context.Context, params v1alpha1.Parameters)
return nil
}
-func (TaintFilter) Run(traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (TaintFilter) Run(_ context.Context, traceLog *slog.Logger, request pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
activations := make(map[string]float64)
stats := make(map[string]lib.FilterWeigherPipelineStepStatistics)
diff --git a/internal/scheduling/pods/plugins/filters/filter_taint_test.go b/internal/scheduling/pods/plugins/filters/filter_taint_test.go
index 1d248b685..1a977096c 100644
--- a/internal/scheduling/pods/plugins/filters/filter_taint_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_taint_test.go
@@ -252,7 +252,7 @@ func TestTaintFilter_Run(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter := &TaintFilter{}
- result, err := filter.Run(slog.Default(), tt.request)
+ result, err := filter.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Errorf("expected Run() to succeed, got error: %v", err)
diff --git a/internal/scheduling/pods/plugins/weighers/binpack.go b/internal/scheduling/pods/plugins/weighers/binpack.go
index 07d310fb8..4c1addff7 100644
--- a/internal/scheduling/pods/plugins/weighers/binpack.go
+++ b/internal/scheduling/pods/plugins/weighers/binpack.go
@@ -4,6 +4,7 @@
package weighers
import (
+ "context"
"errors"
"log/slog"
"math"
@@ -31,7 +32,7 @@ type BinpackingStep struct {
lib.BaseWeigher[api.PodPipelineRequest, BinpackingStepOpts]
}
-func (s *BinpackingStep) Run(traceLog *slog.Logger, request api.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *BinpackingStep) Run(_ context.Context, traceLog *slog.Logger, request api.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
podResources := helpers.GetPodResourceRequests(request.Pod)
diff --git a/internal/scheduling/pods/plugins/weighers/binpack_test.go b/internal/scheduling/pods/plugins/weighers/binpack_test.go
index 198e110c1..48d55570f 100644
--- a/internal/scheduling/pods/plugins/weighers/binpack_test.go
+++ b/internal/scheduling/pods/plugins/weighers/binpack_test.go
@@ -257,7 +257,7 @@ func TestBinpackingStep_Run(t *testing.T) {
},
}
- result, err := tt.step.Run(slog.Default(), tt.request)
+ result, err := tt.step.Run(t.Context(), slog.Default(), tt.request)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
From dc653892590182132712e911ad5ad153dbc9cce0 Mon Sep 17 00:00:00 2001
From: henrichter
Date: Sun, 15 Feb 2026 17:32:03 +0100
Subject: [PATCH 3/5] fix: use NamespacedName instead of ObjectReference
---
internal/scheduling/lib/weigher.go | 9 +++------
internal/scheduling/lib/weigher_test.go | 16 ++++++++--------
.../weighers/netapp_cpu_usage_balancing.go | 4 ++--
.../vmware_anti_affinity_noisy_projects.go | 4 ++--
.../vmware_avoid_long_term_contended_hosts.go | 4 ++--
.../vmware_avoid_short_term_contended_hosts.go | 4 ++--
.../nova/plugins/weighers/vmware_binpack.go | 3 ++-
7 files changed, 21 insertions(+), 23 deletions(-)
diff --git a/internal/scheduling/lib/weigher.go b/internal/scheduling/lib/weigher.go
index 814acf38b..18d9b7c46 100644
--- a/internal/scheduling/lib/weigher.go
+++ b/internal/scheduling/lib/weigher.go
@@ -9,8 +9,8 @@ import (
"fmt"
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
- corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
+ "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -42,16 +42,13 @@ func (s *BaseWeigher[RequestType, Opts]) Validate(ctx context.Context, params v1
}
// Check if all knowledges are ready, and if not, return an error indicating why not.
-func (d *BaseFilterWeigherPipelineStep[RequestType, Opts]) CheckKnowledges(ctx context.Context, kns ...corev1.ObjectReference) error {
+func (d *BaseFilterWeigherPipelineStep[RequestType, Opts]) CheckKnowledges(ctx context.Context, kns ...types.NamespacedName) error {
if d.Client == nil {
return errors.New("kubernetes client not initialized")
}
for _, objRef := range kns {
knowledge := &v1alpha1.Knowledge{}
- if err := d.Client.Get(ctx, client.ObjectKey{
- Name: objRef.Name,
- Namespace: objRef.Namespace,
- }, knowledge); err != nil {
+ if err := d.Client.Get(ctx, objRef, knowledge); err != nil {
return fmt.Errorf("failed to get knowledge %s: %w", objRef.Name, err)
}
// Check if the knowledge status conditions indicate an error.
diff --git a/internal/scheduling/lib/weigher_test.go b/internal/scheduling/lib/weigher_test.go
index dec8e9378..a8efbb59f 100644
--- a/internal/scheduling/lib/weigher_test.go
+++ b/internal/scheduling/lib/weigher_test.go
@@ -9,9 +9,9 @@ import (
"testing"
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
- corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
@@ -107,7 +107,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
tests := []struct {
name string
knowledges []v1alpha1.Knowledge
- refs []corev1.ObjectReference
+ refs []types.NamespacedName
expectError bool
errorMsg string
}{
@@ -130,7 +130,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
},
},
},
- refs: []corev1.ObjectReference{
+ refs: []types.NamespacedName{
{Name: "knowledge1", Namespace: "default"},
},
expectError: false,
@@ -138,7 +138,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
{
name: "knowledge not found",
knowledges: []v1alpha1.Knowledge{},
- refs: []corev1.ObjectReference{
+ refs: []types.NamespacedName{
{Name: "missing-knowledge", Namespace: "default"},
},
expectError: true,
@@ -163,7 +163,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
},
},
},
- refs: []corev1.ObjectReference{
+ refs: []types.NamespacedName{
{Name: "knowledge1", Namespace: "default"},
},
expectError: true,
@@ -188,7 +188,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
},
},
},
- refs: []corev1.ObjectReference{
+ refs: []types.NamespacedName{
{Name: "knowledge1", Namespace: "default"},
},
expectError: true,
@@ -197,7 +197,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
{
name: "empty knowledge list",
knowledges: []v1alpha1.Knowledge{},
- refs: []corev1.ObjectReference{},
+ refs: []types.NamespacedName{},
expectError: false,
},
}
@@ -236,7 +236,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges_NilClient(t *testing.T) {
Client: nil,
}
- err := step.CheckKnowledges(t.Context(), corev1.ObjectReference{Name: "test", Namespace: "default"})
+ err := step.CheckKnowledges(t.Context(), types.NamespacedName{Name: "test", Namespace: "default"})
if err == nil {
t.Error("expected error for nil client but got nil")
diff --git a/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go b/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go
index 44eaba0e0..3b4186843 100644
--- a/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go
+++ b/internal/scheduling/manila/plugins/weighers/netapp_cpu_usage_balancing.go
@@ -12,7 +12,7 @@ import (
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins/storage"
"github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
- corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -54,7 +54,7 @@ func (s *NetappCPUUsageBalancingStep) Init(ctx context.Context, client client.Cl
if err := s.BaseWeigher.Init(ctx, client, weigher); err != nil {
return err
}
- if err := s.CheckKnowledges(ctx, corev1.ObjectReference{Name: "netapp-storage-pool-cpu-usage-manila"}); err != nil {
+ if err := s.CheckKnowledges(ctx, types.NamespacedName{Name: "netapp-storage-pool-cpu-usage-manila"}); err != nil {
return err
}
return nil
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go b/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go
index 5cbdf69e2..446fc5622 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_anti_affinity_noisy_projects.go
@@ -12,7 +12,7 @@ import (
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins/compute"
"github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
- corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -45,7 +45,7 @@ func (s *VMwareAntiAffinityNoisyProjectsStep) Init(ctx context.Context, client c
if err := s.BaseWeigher.Init(ctx, client, weigher); err != nil {
return err
}
- if err := s.CheckKnowledges(ctx, corev1.ObjectReference{Name: "vmware-project-noisiness"}); err != nil {
+ if err := s.CheckKnowledges(ctx, types.NamespacedName{Name: "vmware-project-noisiness"}); err != nil {
return err
}
return nil
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go b/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go
index a97ca8fe9..799ea442f 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_avoid_long_term_contended_hosts.go
@@ -12,7 +12,7 @@ import (
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins/compute"
"github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
- corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -54,7 +54,7 @@ func (s *VMwareAvoidLongTermContendedHostsStep) Init(ctx context.Context, client
if err := s.BaseWeigher.Init(ctx, client, weigher); err != nil {
return err
}
- if err := s.CheckKnowledges(ctx, corev1.ObjectReference{Name: "vmware-long-term-contended-hosts"}); err != nil {
+ if err := s.CheckKnowledges(ctx, types.NamespacedName{Name: "vmware-long-term-contended-hosts"}); err != nil {
return err
}
return nil
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go b/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go
index 3f93bc158..2312cb5ce 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_avoid_short_term_contended_hosts.go
@@ -12,7 +12,7 @@ import (
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins/compute"
"github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
- corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -54,7 +54,7 @@ func (s *VMwareAvoidShortTermContendedHostsStep) Init(ctx context.Context, clien
if err := s.BaseWeigher.Init(ctx, client, weigher); err != nil {
return err
}
- if err := s.CheckKnowledges(ctx, corev1.ObjectReference{Name: "vmware-short-term-contended-hosts"}); err != nil {
+ if err := s.CheckKnowledges(ctx, types.NamespacedName{Name: "vmware-short-term-contended-hosts"}); err != nil {
return err
}
return nil
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_binpack.go b/internal/scheduling/nova/plugins/weighers/vmware_binpack.go
index bdcb46339..17937d4b2 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_binpack.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_binpack.go
@@ -16,6 +16,7 @@ import (
"github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
+ "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -77,7 +78,7 @@ func (s *VMwareBinpackStep) Init(ctx context.Context, client client.Client, weig
return err
}
if err := s.CheckKnowledges(ctx,
- corev1.ObjectReference{Name: "host-utilization"},
+ types.NamespacedName{Name: "host-utilization"},
); err != nil {
return err
}
From 99dc2f86d1089e3331d5b223f944f0939deaa7db Mon Sep 17 00:00:00 2001
From: henrichter
Date: Sun, 15 Feb 2026 17:35:08 +0100
Subject: [PATCH 4/5] feat[extractor]: use DI for extraction
---
internal/knowledge/extractor/controller.go | 31 ++++++++++++++--
internal/knowledge/extractor/monitor.go | 4 +--
internal/knowledge/extractor/monitor_test.go | 18 +++++-----
.../extractor/plugins/compute/host_az.go | 3 +-
.../extractor/plugins/compute/host_az_test.go | 2 +-
.../plugins/compute/host_capabilities.go | 3 +-
.../plugins/compute/host_capabilities_test.go | 2 +-
.../extractor/plugins/compute/host_details.go | 36 +++++++++----------
.../plugins/compute/host_details_test.go | 21 +++++------
.../compute/host_pinned_project_test.go | 2 +-
.../plugins/compute/host_pinned_projects.go | 3 +-
.../plugins/compute/host_utilization.go | 3 +-
.../plugins/compute/host_utilization_test.go | 2 +-
.../compute/libvirt_domain_cpu_steal_pct.go | 3 +-
.../libvirt_domain_cpu_steal_pct_test.go | 2 +-
.../plugins/compute/vm_host_residency.go | 3 +-
.../plugins/compute/vm_host_residency_test.go | 2 +-
.../extractor/plugins/compute/vm_life_span.go | 3 +-
.../plugins/compute/vm_life_span_test.go | 2 +-
.../vrops_hostsystem_contention_long_term.go | 21 ++++++-----
...ps_hostsystem_contention_long_term_test.go | 17 ++++-----
.../vrops_hostsystem_contention_short_term.go | 21 ++++++-----
...s_hostsystem_contention_short_term_test.go | 17 ++++-----
.../compute/vrops_hostsystem_resolver.go | 3 +-
.../compute/vrops_hostsystem_resolver_test.go | 2 +-
.../compute/vrops_project_noisiness.go | 3 +-
.../compute/vrops_project_noisiness_test.go | 2 +-
.../knowledge/extractor/plugins/interface.go | 2 +-
.../plugins/storage/storage_pool_cpu_usage.go | 3 +-
.../storage/storage_pool_cpu_usage_test.go | 2 +-
30 files changed, 128 insertions(+), 110 deletions(-)
diff --git a/internal/knowledge/extractor/controller.go b/internal/knowledge/extractor/controller.go
index f1a641c22..ca1666015 100644
--- a/internal/knowledge/extractor/controller.go
+++ b/internal/knowledge/extractor/controller.go
@@ -10,7 +10,6 @@ import (
"time"
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
-
"github.com/cobaltcore-dev/cortex/internal/knowledge/db"
"github.com/cobaltcore-dev/cortex/pkg/multicluster"
corev1 "k8s.io/api/core/v1"
@@ -78,6 +77,7 @@ func (r *KnowledgeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
// Check if all datasources configured share the same database secret ref.
var databaseSecretRef *corev1.SecretReference
+ var dataSources []*v1alpha1.Datasource
for _, dsRef := range knowledge.Spec.Dependencies.Datasources {
ds := &v1alpha1.Datasource{}
if err := r.Get(ctx, client.ObjectKey{
@@ -118,7 +118,34 @@ func (r *KnowledgeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
}
return ctrl.Result{}, nil
}
+ dataSources = append(dataSources, ds)
+ }
+
+ var knowledgeSources []*v1alpha1.Knowledge
+ for _, knRef := range knowledge.Spec.Dependencies.Knowledges {
+ kn := &v1alpha1.Knowledge{}
+ if err := r.Get(ctx, client.ObjectKey{
+ Namespace: req.Namespace,
+ Name: knRef.Name,
+ }, kn); err != nil {
+ log.Error(err, "failed to get knowledge", "name", knRef.Name)
+ old := knowledge.DeepCopy()
+ meta.SetStatusCondition(&knowledge.Status.Conditions, metav1.Condition{
+ Type: v1alpha1.KnowledgeConditionReady,
+ Status: metav1.ConditionFalse,
+ Reason: "KnowledgeFetchFailed",
+ Message: "failed to get knowledge: " + err.Error(),
+ })
+ patch := client.MergeFrom(old)
+ if err := r.Status().Patch(ctx, knowledge, patch); err != nil {
+ log.Error(err, "failed to patch knowledge status")
+ return ctrl.Result{}, err
+ }
+ return ctrl.Result{}, err
+ }
+ knowledgeSources = append(knowledgeSources, kn)
}
+
// When we have datasources reading from a database, connect to it.
var authenticatedDatasourceDB *db.DB
if databaseSecretRef != nil {
@@ -162,7 +189,7 @@ func (r *KnowledgeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract(dataSources, knowledgeSources)
if err != nil {
log.Error(err, "failed to extract features", "name", knowledge.Spec.Extractor.Name)
old := knowledge.DeepCopy()
diff --git a/internal/knowledge/extractor/monitor.go b/internal/knowledge/extractor/monitor.go
index d74c74d2c..f8c5ee158 100644
--- a/internal/knowledge/extractor/monitor.go
+++ b/internal/knowledge/extractor/monitor.go
@@ -94,10 +94,10 @@ func monitorFeatureExtractor[F plugins.FeatureExtractor](label string, f F, m Mo
}
// Run the wrapped feature extractor and measure the time it takes.
-func (m FeatureExtractorMonitor[F]) Extract() ([]plugins.Feature, error) {
+func (m FeatureExtractorMonitor[F]) Extract(d []*v1alpha1.Datasource, k []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
slog.Info("features: extracting", "extractor", m.label)
- features, err := m.FeatureExtractor.Extract()
+ features, err := m.FeatureExtractor.Extract(d, k)
if err != nil {
return nil, err
}
diff --git a/internal/knowledge/extractor/monitor_test.go b/internal/knowledge/extractor/monitor_test.go
index 28b976945..c17cbbf07 100644
--- a/internal/knowledge/extractor/monitor_test.go
+++ b/internal/knowledge/extractor/monitor_test.go
@@ -26,7 +26,7 @@ func (m *mockFeatureExtractor) Init(datasourceDB *db.DB, client client.Client, s
return m.initError
}
-func (m *mockFeatureExtractor) Extract() ([]plugins.Feature, error) {
+func (m *mockFeatureExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
if m.extractError != nil {
return nil, m.extractError
}
@@ -118,7 +118,7 @@ func TestMonitorFeatureExtractor_Extract_Success(t *testing.T) {
wrappedExtractor := monitorFeatureExtractor("test-extractor", mockExtractor, monitor)
- features, err := wrappedExtractor.Extract()
+ features, err := wrappedExtractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
@@ -154,7 +154,7 @@ func TestMonitorFeatureExtractor_Extract_Error(t *testing.T) {
wrappedExtractor := monitorFeatureExtractor("test-extractor", mockExtractor, monitor)
- features, err := wrappedExtractor.Extract()
+ features, err := wrappedExtractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if !errors.Is(err, expectedError) {
t.Errorf("Expected error %v, got %v", expectedError, err)
}
@@ -171,7 +171,7 @@ func TestMonitorFeatureExtractor_Extract_EmptyFeatures(t *testing.T) {
wrappedExtractor := monitorFeatureExtractor("test-extractor", mockExtractor, monitor)
- features, err := wrappedExtractor.Extract()
+ features, err := wrappedExtractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
@@ -202,7 +202,7 @@ func TestMonitorFeatureExtractor_NilMonitor(t *testing.T) {
wrappedExtractor := monitorFeatureExtractor("test-extractor", mockExtractor, monitor)
// Should not panic even with nil metrics
- features, err := wrappedExtractor.Extract()
+ features, err := wrappedExtractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
@@ -235,7 +235,7 @@ func TestMonitorFeatureExtractor_MultipleExtractions(t *testing.T) {
wrappedExtractor := monitorFeatureExtractor("test-extractor", mockExtractor, monitor)
// First extraction
- features1, err := wrappedExtractor.Extract()
+ features1, err := wrappedExtractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Errorf("First extraction failed: %v", err)
}
@@ -249,7 +249,7 @@ func TestMonitorFeatureExtractor_MultipleExtractions(t *testing.T) {
}
// Second extraction
- features2, err := wrappedExtractor.Extract()
+ features2, err := wrappedExtractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Errorf("Second extraction failed: %v", err)
}
@@ -286,12 +286,12 @@ func TestMonitorFeatureExtractor_DifferentExtractorNames(t *testing.T) {
wrappedExtractor2 := monitorFeatureExtractor("extractor-2", mockExtractor2, monitor)
// Extract from both
- _, err1 := wrappedExtractor1.Extract()
+ _, err1 := wrappedExtractor1.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err1 != nil {
t.Errorf("Extractor 1 failed: %v", err1)
}
- _, err2 := wrappedExtractor2.Extract()
+ _, err2 := wrappedExtractor2.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err2 != nil {
t.Errorf("Extractor 2 failed: %v", err2)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_az.go b/internal/knowledge/extractor/plugins/compute/host_az.go
index 8e690b1c9..f1e913cae 100644
--- a/internal/knowledge/extractor/plugins/compute/host_az.go
+++ b/internal/knowledge/extractor/plugins/compute/host_az.go
@@ -6,6 +6,7 @@ package compute
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -28,6 +29,6 @@ type HostAZExtractor struct {
var hostAZQuery string
// Extract the traits of a compute host from the database.
-func (e *HostAZExtractor) Extract() ([]plugins.Feature, error) {
+func (e *HostAZExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(hostAZQuery)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_az_test.go b/internal/knowledge/extractor/plugins/compute/host_az_test.go
index d3f47bec6..8fc36ac69 100644
--- a/internal/knowledge/extractor/plugins/compute/host_az_test.go
+++ b/internal/knowledge/extractor/plugins/compute/host_az_test.go
@@ -70,7 +70,7 @@ func TestHostAZExtractor_Extract(t *testing.T) {
if err := extractor.Init(&testDB, nil, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_capabilities.go b/internal/knowledge/extractor/plugins/compute/host_capabilities.go
index 7ebf05555..42d5cb0f8 100644
--- a/internal/knowledge/extractor/plugins/compute/host_capabilities.go
+++ b/internal/knowledge/extractor/plugins/compute/host_capabilities.go
@@ -6,6 +6,7 @@ package compute
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -32,6 +33,6 @@ type HostCapabilitiesExtractor struct {
var hostCapabilitiesQuery string
// Extract the traits of a compute host from the database.
-func (e *HostCapabilitiesExtractor) Extract() ([]plugins.Feature, error) {
+func (e *HostCapabilitiesExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(hostCapabilitiesQuery)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_capabilities_test.go b/internal/knowledge/extractor/plugins/compute/host_capabilities_test.go
index 98514dffc..9f3667eb4 100644
--- a/internal/knowledge/extractor/plugins/compute/host_capabilities_test.go
+++ b/internal/knowledge/extractor/plugins/compute/host_capabilities_test.go
@@ -55,7 +55,7 @@ func TestHostCapabilitiesExtractor_Extract(t *testing.T) {
if err := extractor.Init(&testDB, nil, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_details.go b/internal/knowledge/extractor/plugins/compute/host_details.go
index acec0fe4e..b5690f76f 100644
--- a/internal/knowledge/extractor/plugins/compute/host_details.go
+++ b/internal/knowledge/extractor/plugins/compute/host_details.go
@@ -4,14 +4,14 @@
package compute
import (
- "context"
_ "embed"
"errors"
+ "fmt"
+ "slices"
"strings"
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
- "sigs.k8s.io/controller-runtime/pkg/client"
)
type HostDetails struct {
@@ -56,7 +56,7 @@ type HostDetailsExtractor struct {
var hostDetailsQuery string
// Extract the traits of a compute host from the database.
-func (e *HostDetailsExtractor) Extract() ([]plugins.Feature, error) {
+func (e *HostDetailsExtractor) Extract(_ []*v1alpha1.Datasource, k []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
if e.DB == nil {
return nil, errors.New("database connection is not initialized")
}
@@ -66,16 +66,15 @@ func (e *HostDetailsExtractor) Extract() ([]plugins.Feature, error) {
}
// Add the pinned projects to the host details.
- pinnedProjectsKnowledge := &v1alpha1.Knowledge{}
- if err := e.Client.Get(
- context.Background(),
- client.ObjectKey{Name: "host-pinned-projects"},
- pinnedProjectsKnowledge,
- ); err != nil {
- return nil, err
+ name := "host-pinned-projects"
+ idx := slices.IndexFunc(k, func(k *v1alpha1.Knowledge) bool {
+ return k.Name == name
+ })
+ if idx < 0 {
+ return nil, fmt.Errorf("knowledge '%s' not found", name)
}
pinnedProjects, err := v1alpha1.
- UnboxFeatureList[HostPinnedProjects](pinnedProjectsKnowledge.Status.Raw)
+ UnboxFeatureList[HostPinnedProjects](k[idx].Status.Raw)
if err != nil {
return nil, err
}
@@ -99,16 +98,15 @@ func (e *HostDetailsExtractor) Extract() ([]plugins.Feature, error) {
}
// Add the availability zones to the host details.
- azKnowledge := &v1alpha1.Knowledge{}
- if err := e.Client.Get(
- context.Background(),
- client.ObjectKey{Name: "host-az"},
- azKnowledge,
- ); err != nil {
- return nil, err
+ name = "host-az"
+ idx = slices.IndexFunc(k, func(k *v1alpha1.Knowledge) bool {
+ return k.Name == name
+ })
+ if idx < 0 {
+ return nil, fmt.Errorf("knowledge '%s' not found", name)
}
hostAZs, err := v1alpha1.
- UnboxFeatureList[HostAZ](azKnowledge.Status.Raw)
+ UnboxFeatureList[HostAZ](k[idx].Status.Raw)
if err != nil {
return nil, err
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_details_test.go b/internal/knowledge/extractor/plugins/compute/host_details_test.go
index dfc7b3d8d..4f23465c1 100644
--- a/internal/knowledge/extractor/plugins/compute/host_details_test.go
+++ b/internal/knowledge/extractor/plugins/compute/host_details_test.go
@@ -30,11 +30,6 @@ func TestHostDetailsExtractor_Extract(t *testing.T) {
testDB := db.DB{DbMap: dbEnv.DbMap}
defer dbEnv.Close()
- scheme, err := v1alpha1.SchemeBuilder.Build()
- if err != nil {
- t.Fatalf("expected no error, got %v", err)
- }
-
// Create dependency tables
if err := testDB.CreateTable(
testDB.AddTable(nova.Hypervisor{}),
@@ -109,21 +104,21 @@ func TestHostDetailsExtractor_Extract(t *testing.T) {
extractor := &HostDetailsExtractor{}
config := v1alpha1.KnowledgeSpec{}
- client := fake.NewClientBuilder().
- WithScheme(scheme).
- WithObjects(&v1alpha1.Knowledge{
+ client := fake.NewClientBuilder().Build()
+ knowledges := []*v1alpha1.Knowledge{
+ {
ObjectMeta: v1.ObjectMeta{Name: "host-pinned-projects"},
Status: v1alpha1.KnowledgeStatus{Raw: hostPinnedProjects},
- }).
- WithObjects(&v1alpha1.Knowledge{
+ },
+ {
ObjectMeta: v1.ObjectMeta{Name: "host-az"},
Status: v1alpha1.KnowledgeStatus{Raw: hostAvailabilityZones},
- }).
- Build()
+ },
+ }
if err := extractor.Init(&testDB, client, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, knowledges)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_pinned_project_test.go b/internal/knowledge/extractor/plugins/compute/host_pinned_project_test.go
index df569d566..766f053e4 100644
--- a/internal/knowledge/extractor/plugins/compute/host_pinned_project_test.go
+++ b/internal/knowledge/extractor/plugins/compute/host_pinned_project_test.go
@@ -533,7 +533,7 @@ func TestHostPinnedProjectsExtractor_Extract(t *testing.T) {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_pinned_projects.go b/internal/knowledge/extractor/plugins/compute/host_pinned_projects.go
index 9427ea65a..4a505396d 100644
--- a/internal/knowledge/extractor/plugins/compute/host_pinned_projects.go
+++ b/internal/knowledge/extractor/plugins/compute/host_pinned_projects.go
@@ -6,6 +6,7 @@ package compute
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -42,6 +43,6 @@ type HostPinnedProjectsExtractor struct {
var hostPinnedProjectsQuery string
// Extract the pinned projects of a compute host.
-func (e *HostPinnedProjectsExtractor) Extract() ([]plugins.Feature, error) {
+func (e *HostPinnedProjectsExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(hostPinnedProjectsQuery)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_utilization.go b/internal/knowledge/extractor/plugins/compute/host_utilization.go
index 73f7c1ac2..5001d2b8c 100644
--- a/internal/knowledge/extractor/plugins/compute/host_utilization.go
+++ b/internal/knowledge/extractor/plugins/compute/host_utilization.go
@@ -6,6 +6,7 @@ package compute
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -44,6 +45,6 @@ var hostUtilizationQuery string
// Extract the utilization on a compute host.
// Depends on the OpenStack hypervisors to be synced.
-func (e *HostUtilizationExtractor) Extract() ([]plugins.Feature, error) {
+func (e *HostUtilizationExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(hostUtilizationQuery)
}
diff --git a/internal/knowledge/extractor/plugins/compute/host_utilization_test.go b/internal/knowledge/extractor/plugins/compute/host_utilization_test.go
index 984823564..22ae71416 100644
--- a/internal/knowledge/extractor/plugins/compute/host_utilization_test.go
+++ b/internal/knowledge/extractor/plugins/compute/host_utilization_test.go
@@ -208,7 +208,7 @@ func TestHostUtilizationExtractor_Extract(t *testing.T) {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct.go b/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct.go
index 9898c591f..a8012fe42 100644
--- a/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct.go
+++ b/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct.go
@@ -6,6 +6,7 @@ package compute
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -32,6 +33,6 @@ type LibvirtDomainCPUStealPctExtractor struct {
var libvirtDomainCPUStealPctSQL string
// Extract CPU steal time of kvm hosts.
-func (e *LibvirtDomainCPUStealPctExtractor) Extract() ([]plugins.Feature, error) {
+func (e *LibvirtDomainCPUStealPctExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(libvirtDomainCPUStealPctSQL)
}
diff --git a/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct_test.go b/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct_test.go
index bc28218b5..40b3e1d64 100644
--- a/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct_test.go
+++ b/internal/knowledge/extractor/plugins/compute/libvirt_domain_cpu_steal_pct_test.go
@@ -90,7 +90,7 @@ func TestLibvirtDomainCPUStealPctExtractor_Extract(t *testing.T) {
if err := extractor.Init(&testDB, nil, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vm_host_residency.go b/internal/knowledge/extractor/plugins/compute/vm_host_residency.go
index b5c3b6705..877c30b1a 100644
--- a/internal/knowledge/extractor/plugins/compute/vm_host_residency.go
+++ b/internal/knowledge/extractor/plugins/compute/vm_host_residency.go
@@ -9,6 +9,7 @@ import (
"log/slog"
"strings"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
"github.com/cobaltcore-dev/cortex/internal/knowledge/math"
"github.com/prometheus/client_golang/prometheus"
@@ -50,7 +51,7 @@ var vmHostResidencyQuery string
// Extract the time elapsed until the first migration of a virtual machine.
// Depends on the OpenStack servers and migrations to be synced.
-func (e *VMHostResidencyExtractor) Extract() ([]plugins.Feature, error) {
+func (e *VMHostResidencyExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
// This can happen when no datasource is provided that connects to a database.
if e.DB == nil {
return nil, errors.New("database connection is not initialized")
diff --git a/internal/knowledge/extractor/plugins/compute/vm_host_residency_test.go b/internal/knowledge/extractor/plugins/compute/vm_host_residency_test.go
index 1d26a429e..8c0a1305f 100644
--- a/internal/knowledge/extractor/plugins/compute/vm_host_residency_test.go
+++ b/internal/knowledge/extractor/plugins/compute/vm_host_residency_test.go
@@ -69,7 +69,7 @@ func TestVMHostResidencyExtractor_Extract(t *testing.T) {
t.Fatalf("expected no error during initialization, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error during extraction, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vm_life_span.go b/internal/knowledge/extractor/plugins/compute/vm_life_span.go
index 4c7b99197..9ee7efc48 100644
--- a/internal/knowledge/extractor/plugins/compute/vm_life_span.go
+++ b/internal/knowledge/extractor/plugins/compute/vm_life_span.go
@@ -9,6 +9,7 @@ import (
"log/slog"
"strings"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
"github.com/cobaltcore-dev/cortex/internal/knowledge/math"
"github.com/prometheus/client_golang/prometheus"
@@ -94,7 +95,7 @@ var vmLifeSpanQuery string
// Extract the time elapsed until the first migration of a virtual machine.
// Depends on the OpenStack servers and migrations to be synced.
-func (e *VMLifeSpanHistogramExtractor) Extract() ([]plugins.Feature, error) {
+func (e *VMLifeSpanHistogramExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
// This can happen when no datasource is provided that connects to a database.
if e.DB == nil {
return nil, errors.New("database connection is not initialized")
diff --git a/internal/knowledge/extractor/plugins/compute/vm_life_span_test.go b/internal/knowledge/extractor/plugins/compute/vm_life_span_test.go
index a6b582b51..e4df31a7b 100644
--- a/internal/knowledge/extractor/plugins/compute/vm_life_span_test.go
+++ b/internal/knowledge/extractor/plugins/compute/vm_life_span_test.go
@@ -65,7 +65,7 @@ func TestVMLifeSpanExtractor_Extract(t *testing.T) {
t.Fatalf("expected no error during initialization, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error during extraction, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term.go b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term.go
index ff53bcb3c..9c1fd1956 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term.go
@@ -4,14 +4,14 @@
package compute
import (
- "context"
_ "embed"
"errors"
+ "fmt"
"log/slog"
+ "slices"
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
- "sigs.k8s.io/controller-runtime/pkg/client"
)
// Feature that maps CPU contention of vROps hostsystems.
@@ -36,7 +36,7 @@ var vropsHostsystemContentionLongTermSQL string
// Extract long term CPU contention of hostsystems.
// Depends on resolved vROps hostsystems (feature_vrops_resolved_hostsystem).
-func (e *VROpsHostsystemContentionLongTermExtractor) Extract() ([]plugins.Feature, error) {
+func (e *VROpsHostsystemContentionLongTermExtractor) Extract(_ []*v1alpha1.Datasource, k []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
if e.DB == nil {
return nil, errors.New("database connection is not initialized")
}
@@ -50,16 +50,15 @@ func (e *VROpsHostsystemContentionLongTermExtractor) Extract() ([]plugins.Featur
return nil, err
}
- resolvedHostsystemsKnowledge := &v1alpha1.Knowledge{}
- if err := e.Client.Get(
- context.Background(),
- client.ObjectKey{Name: "vmware-resolved-hostsystems"},
- resolvedHostsystemsKnowledge,
- ); err != nil {
- return nil, err
+ name := "vmware-resolved-hostsystems"
+ idx := slices.IndexFunc(k, func(k *v1alpha1.Knowledge) bool {
+ return k.Name == name
+ })
+ if idx < 0 {
+ return nil, fmt.Errorf("knowledge '%s' not found", name)
}
resolvedHostsystems, err := v1alpha1.
- UnboxFeatureList[ResolvedVROpsHostsystem](resolvedHostsystemsKnowledge.Status.Raw)
+ UnboxFeatureList[ResolvedVROpsHostsystem](k[idx].Status.Raw)
if err != nil {
return nil, err
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term_test.go b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term_test.go
index b894c7235..1bf2e9d05 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term_test.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_long_term_test.go
@@ -28,11 +28,6 @@ func TestVROpsHostsystemContentionLongTermExtractor_Extract(t *testing.T) {
testDB := db.DB{DbMap: dbEnv.DbMap}
defer dbEnv.Close()
- scheme, err := v1alpha1.SchemeBuilder.Build()
- if err != nil {
- t.Fatalf("expected no error, got %v", err)
- }
-
// Create dependency tables
if err := testDB.CreateTable(
testDB.AddTable(prometheus.VROpsHostMetric{}),
@@ -59,17 +54,17 @@ func TestVROpsHostsystemContentionLongTermExtractor_Extract(t *testing.T) {
extractor := &VROpsHostsystemContentionLongTermExtractor{}
config := v1alpha1.KnowledgeSpec{}
- client := fake.NewClientBuilder().
- WithScheme(scheme).
- WithObjects(&v1alpha1.Knowledge{
+ client := fake.NewClientBuilder().Build()
+ knowledges := []*v1alpha1.Knowledge{
+ {
ObjectMeta: v1.ObjectMeta{Name: "vmware-resolved-hostsystems"},
Status: v1alpha1.KnowledgeStatus{Raw: vropsResolvedHostsystems},
- }).
- Build()
+ },
+ }
if err := extractor.Init(&testDB, client, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, knowledges)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go
index e9636063f..5349f0f17 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go
@@ -4,14 +4,14 @@
package compute
import (
- "context"
_ "embed"
"errors"
+ "fmt"
"log/slog"
+ "slices"
"github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
- "sigs.k8s.io/controller-runtime/pkg/client"
)
// Feature that maps CPU contention of vROps hostsystems.
@@ -36,7 +36,7 @@ var vropsHostsystemContentionShortTermSQL string
// Extract short term CPU contention of hostsystems.
// Depends on resolved vROps hostsystems (feature_vrops_resolved_hostsystem).
-func (e *VROpsHostsystemContentionShortTermExtractor) Extract() ([]plugins.Feature, error) {
+func (e *VROpsHostsystemContentionShortTermExtractor) Extract(_ []*v1alpha1.Datasource, k []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
if e.DB == nil {
return nil, errors.New("database connection is not initialized")
}
@@ -50,16 +50,15 @@ func (e *VROpsHostsystemContentionShortTermExtractor) Extract() ([]plugins.Featu
return nil, err
}
- resolvedHostsystemsKnowledge := &v1alpha1.Knowledge{}
- if err := e.Client.Get(
- context.Background(),
- client.ObjectKey{Name: "vmware-resolved-hostsystems"},
- resolvedHostsystemsKnowledge,
- ); err != nil {
- return nil, err
+ name := "vmware-resolved-hostsystems"
+ idx := slices.IndexFunc(k, func(k *v1alpha1.Knowledge) bool {
+ return k.Name == name
+ })
+ if idx < 0 {
+ return nil, fmt.Errorf("knowledge '%s' not found", name)
}
resolvedHostsystems, err := v1alpha1.
- UnboxFeatureList[ResolvedVROpsHostsystem](resolvedHostsystemsKnowledge.Status.Raw)
+ UnboxFeatureList[ResolvedVROpsHostsystem](k[idx].Status.Raw)
if err != nil {
return nil, err
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term_test.go b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term_test.go
index 7e5ea652a..182902ae6 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term_test.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term_test.go
@@ -28,11 +28,6 @@ func TestVROpsHostsystemContentionShortTermExtractor_Extract(t *testing.T) {
testDB := db.DB{DbMap: dbEnv.DbMap}
defer dbEnv.Close()
- scheme, err := v1alpha1.SchemeBuilder.Build()
- if err != nil {
- t.Fatalf("expected no error, got %v", err)
- }
-
// Create dependency tables
if err := testDB.CreateTable(
testDB.AddTable(prometheus.VROpsHostMetric{}),
@@ -59,17 +54,17 @@ func TestVROpsHostsystemContentionShortTermExtractor_Extract(t *testing.T) {
extractor := &VROpsHostsystemContentionShortTermExtractor{}
config := v1alpha1.KnowledgeSpec{}
- client := fake.NewClientBuilder().
- WithScheme(scheme).
- WithObjects(&v1alpha1.Knowledge{
+ client := fake.NewClientBuilder().Build()
+ knowledges := []*v1alpha1.Knowledge{
+ {
ObjectMeta: v1.ObjectMeta{Name: "vmware-resolved-hostsystems"},
Status: v1alpha1.KnowledgeStatus{Raw: vropsResolvedHostsystems},
- }).
- Build()
+ },
+ }
if err := extractor.Init(&testDB, client, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, knowledges)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver.go b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver.go
index ada567854..432f4f619 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver.go
@@ -6,6 +6,7 @@ package compute
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -30,6 +31,6 @@ type VROpsHostsystemResolver struct {
var vropsHostsystemSQL string
// Resolve vROps hostsystems to Nova compute hosts.
-func (e *VROpsHostsystemResolver) Extract() ([]plugins.Feature, error) {
+func (e *VROpsHostsystemResolver) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(vropsHostsystemSQL)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver_test.go b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver_test.go
index 6a58439f8..272ed055c 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver_test.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_resolver_test.go
@@ -55,7 +55,7 @@ func TestVROpsHostsystemResolver_Extract(t *testing.T) {
if err := extractor.Init(&testDB, nil, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness.go b/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness.go
index 638c76e26..fc33e3972 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness.go
@@ -6,6 +6,7 @@ package compute
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -30,6 +31,6 @@ type VROpsProjectNoisinessExtractor struct {
//go:embed vrops_project_noisiness.sql
var vropsProjectNoisinessSQL string
-func (e *VROpsProjectNoisinessExtractor) Extract() ([]plugins.Feature, error) {
+func (e *VROpsProjectNoisinessExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(vropsProjectNoisinessSQL)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness_test.go b/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness_test.go
index b0909a434..39d5d9313 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness_test.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_project_noisiness_test.go
@@ -79,7 +79,7 @@ func TestVROpsProjectNoisinessExtractor_Extract(t *testing.T) {
if err := extractor.Init(&testDB, nil, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
diff --git a/internal/knowledge/extractor/plugins/interface.go b/internal/knowledge/extractor/plugins/interface.go
index 049acd9f2..5f325ae6d 100644
--- a/internal/knowledge/extractor/plugins/interface.go
+++ b/internal/knowledge/extractor/plugins/interface.go
@@ -14,7 +14,7 @@ type FeatureExtractor interface {
// Configure the feature extractor with a spec and (optional) databases.
Init(datasourceDB *db.DB, client client.Client, spec v1alpha1.KnowledgeSpec) error
// Extract features from the given data.
- Extract() ([]Feature, error)
+ Extract(d []*v1alpha1.Datasource, k []*v1alpha1.Knowledge) ([]Feature, error)
}
type Feature any
diff --git a/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage.go b/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage.go
index 326034692..803697d89 100644
--- a/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage.go
+++ b/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage.go
@@ -6,6 +6,7 @@ package storage
import (
_ "embed"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
)
@@ -32,6 +33,6 @@ type StoragePoolCPUUsageExtractor struct {
var storagePoolCPUUsageQuery string
// Extract the CPU usage of a storage pool.
-func (e *StoragePoolCPUUsageExtractor) Extract() ([]plugins.Feature, error) {
+func (e *StoragePoolCPUUsageExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
return e.ExtractSQL(storagePoolCPUUsageQuery)
}
diff --git a/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage_test.go b/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage_test.go
index 41b985c39..7f0898eb1 100644
--- a/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage_test.go
+++ b/internal/knowledge/extractor/plugins/storage/storage_pool_cpu_usage_test.go
@@ -66,7 +66,7 @@ func TestStoragePoolCPUUsageExtractor_Extract(t *testing.T) {
if err := extractor.Init(&testDB, nil, config); err != nil {
t.Fatalf("expected no error, got %v", err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
From 961c1605f9c3909f4a841dd77eedae43aaed997a Mon Sep 17 00:00:00 2001
From: henrichter
Date: Sun, 15 Feb 2026 17:35:37 +0100
Subject: [PATCH 5/5] feat: add generic metric, extractor, weigher/filter
---
.../plugins/prometheus/supported_syncers.go | 1 +
.../datasources/plugins/prometheus/types.go | 34 ++++
internal/knowledge/extractor/plugins/base.go | 4 +-
.../plugins/compute/flavor_groups.go | 3 +-
.../plugins/compute/flavor_groups_test.go | 4 +-
.../vrops_hostsystem_contention_short_term.go | 2 +-
.../knowledge/extractor/plugins/generic.go | 39 ++++
.../extractor/plugins/generic_test.go | 77 ++++++++
.../extractor/supported_extractors.go | 2 +
.../pods/plugins/filters/filter_generic.go | 76 ++++++++
.../plugins/filters/filter_generic_test.go | 181 +++++++++++++++++
.../pods/plugins/weighers/generic.go | 74 +++++++
.../pods/plugins/weighers/generic_test.go | 182 ++++++++++++++++++
13 files changed, 673 insertions(+), 6 deletions(-)
create mode 100644 internal/knowledge/extractor/plugins/generic.go
create mode 100644 internal/knowledge/extractor/plugins/generic_test.go
create mode 100644 internal/scheduling/pods/plugins/filters/filter_generic.go
create mode 100644 internal/scheduling/pods/plugins/filters/filter_generic_test.go
create mode 100644 internal/scheduling/pods/plugins/weighers/generic.go
create mode 100644 internal/scheduling/pods/plugins/weighers/generic_test.go
diff --git a/internal/knowledge/datasources/plugins/prometheus/supported_syncers.go b/internal/knowledge/datasources/plugins/prometheus/supported_syncers.go
index c15b23a47..83c9645c8 100644
--- a/internal/knowledge/datasources/plugins/prometheus/supported_syncers.go
+++ b/internal/knowledge/datasources/plugins/prometheus/supported_syncers.go
@@ -26,4 +26,5 @@ var supportedMetricSyncers = map[string]func(
"netapp_node_metric": newTypedSyncer[NetAppNodeMetric],
"netapp_volume_aggregate_labels_metric": newTypedSyncer[NetAppVolumeAggrLabelsMetric],
"kvm_libvirt_domain_metric": newTypedSyncer[KVMDomainMetric],
+ "generic": newTypedSyncer[GenericMetric],
}
diff --git a/internal/knowledge/datasources/plugins/prometheus/types.go b/internal/knowledge/datasources/plugins/prometheus/types.go
index 9fa3b78e9..17238bbaf 100644
--- a/internal/knowledge/datasources/plugins/prometheus/types.go
+++ b/internal/knowledge/datasources/plugins/prometheus/types.go
@@ -292,3 +292,37 @@ func (m NetAppVolumeAggrLabelsMetric) With(n string, t time.Time, v float64) Pro
m.Value = v
return m
}
+
+type GenericMetric struct {
+ Name string `json:"name" db:"name"`
+ Host string `json:"host" db:"host"`
+ Value float64 `json:"value" db:"value"`
+ Timestamp time.Time `json:"timestamp" db:"timestamp"`
+}
+
+func (m GenericMetric) GetName() string {
+ return m.Name
+}
+
+func (m GenericMetric) GetValue() float64 {
+ return m.Value
+}
+
+func (m GenericMetric) GetTimestamp() time.Time {
+ return m.Timestamp
+}
+
+func (m GenericMetric) TableName() string {
+ return "generic"
+}
+
+func (m GenericMetric) Indexes() map[string][]string {
+ return nil
+}
+
+func (m GenericMetric) With(alias string, t time.Time, v float64) PrometheusMetric {
+ m.Name = alias
+ m.Timestamp = t
+ m.Value = v
+ return m
+}
diff --git a/internal/knowledge/extractor/plugins/base.go b/internal/knowledge/extractor/plugins/base.go
index 29a7085e2..f1e409b23 100644
--- a/internal/knowledge/extractor/plugins/base.go
+++ b/internal/knowledge/extractor/plugins/base.go
@@ -42,13 +42,13 @@ func (e *BaseExtractor[Opts, Feature]) Init(
}
// Extract the features directly from an sql query.
-func (e *BaseExtractor[Opts, F]) ExtractSQL(query string) ([]Feature, error) {
+func (e *BaseExtractor[Opts, F]) ExtractSQL(query string, args ...interface{}) ([]Feature, error) {
// This can happen when no datasource is provided that connects to a database.
if e.DB == nil {
return nil, errors.New("database connection is not initialized")
}
var features []F
- if _, err := e.DB.Select(&features, query); err != nil {
+ if _, err := e.DB.Select(&features, query, args...); err != nil {
return nil, err
}
return e.Extracted(features)
diff --git a/internal/knowledge/extractor/plugins/compute/flavor_groups.go b/internal/knowledge/extractor/plugins/compute/flavor_groups.go
index 1dacb9d38..5f98cea8f 100644
--- a/internal/knowledge/extractor/plugins/compute/flavor_groups.go
+++ b/internal/knowledge/extractor/plugins/compute/flavor_groups.go
@@ -9,6 +9,7 @@ import (
"errors"
"sort"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
"github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
ctrl "sigs.k8s.io/controller-runtime"
)
@@ -76,7 +77,7 @@ var flavorGroupsQuery string
var flavorGroupLog = ctrl.Log.WithName("flavor_group_extractor")
// Extract flavor groups from the database.
-func (e *FlavorGroupExtractor) Extract() ([]plugins.Feature, error) {
+func (e *FlavorGroupExtractor) Extract(_ []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]plugins.Feature, error) {
if e.DB == nil {
return nil, errors.New("database connection is not initialized")
}
diff --git a/internal/knowledge/extractor/plugins/compute/flavor_groups_test.go b/internal/knowledge/extractor/plugins/compute/flavor_groups_test.go
index 3c68c4315..709d52073 100644
--- a/internal/knowledge/extractor/plugins/compute/flavor_groups_test.go
+++ b/internal/knowledge/extractor/plugins/compute/flavor_groups_test.go
@@ -125,7 +125,7 @@ func TestFlavorGroupExtractor_Extract(t *testing.T) {
t.Fatal(err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatal(err)
}
@@ -360,7 +360,7 @@ func TestFlavorGroupExtractor_RamCoreRatio_FixedRatio(t *testing.T) {
t.Fatal(err)
}
- features, err := extractor.Extract()
+ features, err := extractor.Extract([]*v1alpha1.Datasource{}, []*v1alpha1.Knowledge{})
if err != nil {
t.Fatal(err)
}
diff --git a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go
index 5349f0f17..9ebd61a6f 100644
--- a/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go
+++ b/internal/knowledge/extractor/plugins/compute/vrops_hostsystem_contention_short_term.go
@@ -26,7 +26,7 @@ type VROpsHostsystemContentionShortTerm struct {
type VROpsHostsystemContentionShortTermExtractor struct {
// Common base for all extractors that provides standard functionality.
plugins.BaseExtractor[
- struct{}, // No options passed through yaml config
+ struct{}, // No options passed through yaml config
VROpsHostsystemContentionShortTerm, // Feature model
]
}
diff --git a/internal/knowledge/extractor/plugins/generic.go b/internal/knowledge/extractor/plugins/generic.go
new file mode 100644
index 000000000..2a20569fd
--- /dev/null
+++ b/internal/knowledge/extractor/plugins/generic.go
@@ -0,0 +1,39 @@
+// Copyright SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package plugins
+
+import (
+ _ "embed"
+ "errors"
+
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
+)
+
+type Generic struct {
+ Host string `db:"host"`
+ Value float64 `db:"value"`
+}
+
+type GenericExtractor struct {
+ BaseExtractor[
+ struct{},
+ Generic,
+ ]
+}
+
+func (e *GenericExtractor) Extract(d []*v1alpha1.Datasource, _ []*v1alpha1.Knowledge) ([]Feature, error) {
+ if len(d) != 1 {
+ return nil, errors.New("generic Knowledge requires exactly one datasource")
+ }
+ dsSpec := &d[0].Spec
+ if dsSpec.Type != v1alpha1.DatasourceTypePrometheus {
+ return nil, errors.New("unsupported datasource type: expected prometheus")
+ }
+ name := dsSpec.Prometheus.Alias
+ if name == "" {
+ return nil, errors.New("prometheus datasource alias cannot be empty")
+ }
+ query := "SELECT host, value FROM generic WHERE name = ?"
+ return e.ExtractSQL(query, name)
+}
diff --git a/internal/knowledge/extractor/plugins/generic_test.go b/internal/knowledge/extractor/plugins/generic_test.go
new file mode 100644
index 000000000..59e5a1034
--- /dev/null
+++ b/internal/knowledge/extractor/plugins/generic_test.go
@@ -0,0 +1,77 @@
+// Copyright SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package plugins
+
+import (
+ "slices"
+ "testing"
+
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
+ "github.com/cobaltcore-dev/cortex/internal/knowledge/datasources/plugins/prometheus"
+ "github.com/cobaltcore-dev/cortex/internal/knowledge/db"
+ testlibDB "github.com/cobaltcore-dev/cortex/internal/knowledge/db/testing"
+)
+
+func TestGenericExtractor_Extract(t *testing.T) {
+ dbEnv := testlibDB.SetupDBEnv(t)
+ testDB := db.DB{DbMap: dbEnv.DbMap}
+ defer dbEnv.Close()
+ // Create dependency table
+ if err := testDB.CreateTable(
+ testDB.AddTable(prometheus.GenericMetric{}),
+ ); err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ metrics := []any{
+ &prometheus.GenericMetric{Name: "node_cpu_seconds_total", Host: "node-01", Value: 0.81},
+ &prometheus.GenericMetric{Name: "node_cpu_seconds_total", Host: "node-02", Value: 0.37},
+ }
+ if err := testDB.Insert(metrics...); err != nil {
+ t.Fatalf("failed to insert manila storage pools: %v", err)
+ }
+
+ extractor := &GenericExtractor{}
+ config := v1alpha1.KnowledgeSpec{}
+ if err := extractor.Init(&testDB, nil, config); err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+ datasources := []*v1alpha1.Datasource{
+ {
+ Spec: v1alpha1.DatasourceSpec{
+ Type: v1alpha1.DatasourceTypePrometheus,
+ Prometheus: v1alpha1.PrometheusDatasource{
+ Alias: "node_cpu_seconds_total",
+ },
+ },
+ },
+ }
+
+ features, err := extractor.Extract(datasources, []*v1alpha1.Knowledge{})
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ var actual []Generic
+ for _, f := range features {
+ actual = append(actual, f.(Generic))
+ }
+
+ expected := []Generic{
+ {Host: "node-01", Value: 0.81},
+ {Host: "node-02", Value: 0.37},
+ }
+
+ if len(actual) != len(expected) {
+ t.Errorf("expected %d rows, got %d", len(expected), len(actual))
+ }
+
+ for _, exp := range expected {
+ if !slices.ContainsFunc(actual, func(m Generic) bool {
+ return m.Host == exp.Host && m.Value == exp.Value
+ }) {
+ t.Errorf("expected to find %+v in actual results %+v", exp, actual)
+ }
+ }
+}
diff --git a/internal/knowledge/extractor/supported_extractors.go b/internal/knowledge/extractor/supported_extractors.go
index 6f1cb2fd2..eb7d69ffa 100644
--- a/internal/knowledge/extractor/supported_extractors.go
+++ b/internal/knowledge/extractor/supported_extractors.go
@@ -26,4 +26,6 @@ var supportedExtractors = map[string]plugins.FeatureExtractor{
"flavor_groups": &compute.FlavorGroupExtractor{},
"netapp_storage_pool_cpu_usage_extractor": &storage.StoragePoolCPUUsageExtractor{},
+
+ "generic": &plugins.GenericExtractor{},
}
diff --git a/internal/scheduling/pods/plugins/filters/filter_generic.go b/internal/scheduling/pods/plugins/filters/filter_generic.go
new file mode 100644
index 000000000..516aacc6b
--- /dev/null
+++ b/internal/scheduling/pods/plugins/filters/filter_generic.go
@@ -0,0 +1,76 @@
+// Copyright SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package filters
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+
+ "github.com/cobaltcore-dev/cortex/api/external/pods"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
+ "github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
+ "github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type GenericFilterStepOpts struct {
+ Knowledge string `json:"knowledge"`
+}
+
+func (o GenericFilterStepOpts) Validate() error {
+ if o.Knowledge == "" {
+ return fmt.Errorf("knowledge name must be provided")
+ }
+ return nil
+}
+
+type GenericFilterStep struct {
+ lib.BaseFilter[pods.PodPipelineRequest, GenericFilterStepOpts]
+ knowledgeRef types.NamespacedName
+}
+
+func (f *GenericFilterStep) Init(ctx context.Context, client client.Client, spec v1alpha1.FilterSpec) error {
+ if err := f.BaseFilter.Init(ctx, client, spec); err != nil {
+ return err
+ }
+ knowledgeRef := types.NamespacedName{Name: f.Options.Knowledge}
+ if err := f.CheckKnowledges(ctx, knowledgeRef); err != nil {
+ return err
+ }
+ f.knowledgeRef = knowledgeRef
+
+ return nil
+}
+
+func (f *GenericFilterStep) Run(ctx context.Context, _ *slog.Logger, req pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+ knowledge := v1alpha1.Knowledge{}
+ if err := f.Client.Get(ctx, f.knowledgeRef, &knowledge); err != nil {
+ return nil, err
+ }
+
+ nodeFeatures, err := v1alpha1.
+ UnboxFeatureList[plugins.Generic](knowledge.Status.Raw)
+ if err != nil {
+ return nil, err
+ }
+
+ result := f.IncludeAllHostsFromRequest(req)
+ for _, nodeFeature := range nodeFeatures {
+ host := nodeFeature.Host
+ if _, exists := result.Activations[host]; !exists {
+ continue
+ }
+ if nodeFeature.Value == 1.0 {
+ delete(result.Activations, host)
+ }
+ }
+
+ return result, nil
+}
+
+func init() {
+ Index["generic"] = func() PodFilter { return &GenericFilterStep{} }
+}
diff --git a/internal/scheduling/pods/plugins/filters/filter_generic_test.go b/internal/scheduling/pods/plugins/filters/filter_generic_test.go
new file mode 100644
index 000000000..faf8619a2
--- /dev/null
+++ b/internal/scheduling/pods/plugins/filters/filter_generic_test.go
@@ -0,0 +1,181 @@
+// Copyright SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package filters
+
+import (
+ "testing"
+
+ "github.com/cobaltcore-dev/cortex/api/external/pods"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
+ "github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func TestGenericFilterStepOpts_Validate(t *testing.T) {
+ tests := []struct {
+ name string
+ opts GenericFilterStepOpts
+ expectError bool
+ }{
+ {
+ name: "knowledge set",
+ opts: GenericFilterStepOpts{
+ Knowledge: "my-knowledge",
+ },
+ expectError: false,
+ },
+ {
+ name: "knowledge unset",
+ opts: GenericFilterStepOpts{
+ Knowledge: "",
+ },
+ expectError: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := tt.opts.Validate()
+ if tt.expectError && err == nil {
+ t.Error("expected error, got nil")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+ })
+ }
+}
+
+func TestGenericFilterStep_Init(t *testing.T) {
+ tests := []struct {
+ name string
+ knowledge v1alpha1.Knowledge
+ expectError bool
+ }{
+ {
+ name: "knowledge not found",
+ knowledge: v1alpha1.Knowledge{},
+ expectError: true,
+ },
+ {
+ name: "knowledge found, but not ready",
+ knowledge: v1alpha1.Knowledge{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-knowledge",
+ },
+ Status: v1alpha1.KnowledgeStatus{
+ RawLength: 0,
+ },
+ },
+ expectError: true,
+ },
+ {
+ name: "knowledge found and ready",
+ knowledge: v1alpha1.Knowledge{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-knowledge",
+ },
+ Status: v1alpha1.KnowledgeStatus{
+ RawLength: 2,
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ scheme, err := v1alpha1.SchemeBuilder.Build()
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+ client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&tt.knowledge).Build()
+ spec := v1alpha1.FilterSpec{
+ Params: []v1alpha1.Parameter{
+ {Key: "knowledge", StringValue: &tt.knowledge.Name},
+ },
+ }
+ weigher := &GenericFilterStep{}
+ err = weigher.Init(t.Context(), client, spec)
+ if tt.expectError && err == nil {
+ t.Error("expected error, got nil")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+ })
+ }
+}
+
+func TestGenericFilterStep_Run(t *testing.T) {
+ scheme, err := v1alpha1.SchemeBuilder.Build()
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ features, err := v1alpha1.BoxFeatureList([]any{
+ &plugins.Generic{Host: "node1", Value: 1.0},
+ &plugins.Generic{Host: "node2", Value: 0.0},
+ })
+ if err != nil {
+ t.Fatalf("failed to box features: %v", err)
+ }
+
+ knowledge := v1alpha1.Knowledge{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-knowledge",
+ },
+ Status: v1alpha1.KnowledgeStatus{
+ Conditions: []metav1.Condition{
+ {
+ Type: v1alpha1.KnowledgeConditionReady,
+ },
+ },
+ Raw: features,
+ RawLength: 2,
+ },
+ }
+ client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&knowledge).Build()
+ spec := v1alpha1.FilterSpec{
+ Params: []v1alpha1.Parameter{
+ {Key: "knowledge", StringValue: &knowledge.Name},
+ },
+ }
+ weigher := &GenericFilterStep{}
+ err = weigher.Init(t.Context(), client, spec)
+ if err != nil {
+ t.Fatalf("failed to initialize weigher: %v", err)
+ }
+ request := pods.PodPipelineRequest{
+ Nodes: []corev1.Node{
+ {
+ ObjectMeta: metav1.ObjectMeta{Name: "node1"},
+ },
+ {
+ ObjectMeta: metav1.ObjectMeta{Name: "node2"},
+ },
+ {
+ ObjectMeta: metav1.ObjectMeta{Name: "node3"},
+ },
+ },
+ Pod: corev1.Pod{
+ ObjectMeta: metav1.ObjectMeta{Name: "test-pod"},
+ },
+ }
+
+ actual, err := weigher.Run(t.Context(), nil, request)
+ if err != nil {
+ t.Fatalf("failed to run weigher: %v", err)
+ }
+ expected := map[string]float64{
+ "node2": 0.0,
+ "node3": 0.0,
+ }
+ for node, expectedWeight := range expected {
+ if actual.Activations[node] != expectedWeight {
+ t.Errorf("expected weight for node %s to be %f, got %f", node, expectedWeight, actual.Activations[node])
+ }
+ }
+}
diff --git a/internal/scheduling/pods/plugins/weighers/generic.go b/internal/scheduling/pods/plugins/weighers/generic.go
new file mode 100644
index 000000000..f89ec3d91
--- /dev/null
+++ b/internal/scheduling/pods/plugins/weighers/generic.go
@@ -0,0 +1,74 @@
+// Copyright SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package weighers
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+
+ "github.com/cobaltcore-dev/cortex/api/external/pods"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
+ "github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
+ "github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type GenericWeigherStepOpts struct {
+ Knowledge string `json:"knowledge"`
+}
+
+func (o GenericWeigherStepOpts) Validate() error {
+ if o.Knowledge == "" {
+ return fmt.Errorf("knowledge name must be provided")
+ }
+ return nil
+}
+
+type GenericWeigherStep struct {
+ lib.BaseWeigher[pods.PodPipelineRequest, GenericWeigherStepOpts]
+ knowledgeRef types.NamespacedName
+}
+
+func (w *GenericWeigherStep) Init(ctx context.Context, client client.Client, spec v1alpha1.WeigherSpec) error {
+ if err := w.BaseWeigher.Init(ctx, client, spec); err != nil {
+ return err
+ }
+ knowledgeRef := types.NamespacedName{Name: w.Options.Knowledge}
+ if err := w.CheckKnowledges(ctx, knowledgeRef); err != nil {
+ return err
+ }
+ w.knowledgeRef = knowledgeRef
+
+ return nil
+}
+
+func (w *GenericWeigherStep) Run(ctx context.Context, _ *slog.Logger, req pods.PodPipelineRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+ knowledge := v1alpha1.Knowledge{}
+ if err := w.Client.Get(ctx, w.knowledgeRef, &knowledge); err != nil {
+ return nil, err
+ }
+
+ nodeFeatures, err := v1alpha1.
+ UnboxFeatureList[plugins.Generic](knowledge.Status.Raw)
+ if err != nil {
+ return nil, err
+ }
+
+ result := w.IncludeAllHostsFromRequest(req)
+ for _, nodeFeature := range nodeFeatures {
+ host := nodeFeature.Host
+ if _, exists := result.Activations[host]; !exists {
+ continue
+ }
+ result.Activations[host] = nodeFeature.Value
+ }
+
+ return result, nil
+}
+
+func init() {
+ Index["generic"] = func() PodWeigher { return &GenericWeigherStep{} }
+}
diff --git a/internal/scheduling/pods/plugins/weighers/generic_test.go b/internal/scheduling/pods/plugins/weighers/generic_test.go
new file mode 100644
index 000000000..c64c787aa
--- /dev/null
+++ b/internal/scheduling/pods/plugins/weighers/generic_test.go
@@ -0,0 +1,182 @@
+// Copyright SAP SE
+// SPDX-License-Identifier: Apache-2.0
+
+package weighers
+
+import (
+ "testing"
+
+ "github.com/cobaltcore-dev/cortex/api/external/pods"
+ "github.com/cobaltcore-dev/cortex/api/v1alpha1"
+ "github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func TestGenericWeigherStepOpts_Validate(t *testing.T) {
+ tests := []struct {
+ name string
+ opts GenericWeigherStepOpts
+ expectError bool
+ }{
+ {
+ name: "knowledge set",
+ opts: GenericWeigherStepOpts{
+ Knowledge: "my-knowledge",
+ },
+ expectError: false,
+ },
+ {
+ name: "knowledge unset",
+ opts: GenericWeigherStepOpts{
+ Knowledge: "",
+ },
+ expectError: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := tt.opts.Validate()
+ if tt.expectError && err == nil {
+ t.Error("expected error, got nil")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+ })
+ }
+}
+
+func TestGenericWeigherStep_Init(t *testing.T) {
+ tests := []struct {
+ name string
+ knowledge v1alpha1.Knowledge
+ expectError bool
+ }{
+ {
+ name: "knowledge not found",
+ knowledge: v1alpha1.Knowledge{},
+ expectError: true,
+ },
+ {
+ name: "knowledge found, but not ready",
+ knowledge: v1alpha1.Knowledge{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-knowledge",
+ },
+ Status: v1alpha1.KnowledgeStatus{
+ RawLength: 0,
+ },
+ },
+ expectError: true,
+ },
+ {
+ name: "knowledge found and ready",
+ knowledge: v1alpha1.Knowledge{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-knowledge",
+ },
+ Status: v1alpha1.KnowledgeStatus{
+ RawLength: 2,
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ scheme, err := v1alpha1.SchemeBuilder.Build()
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+ client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&tt.knowledge).Build()
+ spec := v1alpha1.WeigherSpec{
+ Params: []v1alpha1.Parameter{
+ {Key: "knowledge", StringValue: &tt.knowledge.Name},
+ },
+ }
+ weigher := &GenericWeigherStep{}
+ err = weigher.Init(t.Context(), client, spec)
+ if tt.expectError && err == nil {
+ t.Error("expected error, got nil")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+ })
+ }
+}
+
+func TestGenericWeigherStep_Run(t *testing.T) {
+ scheme, err := v1alpha1.SchemeBuilder.Build()
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ features, err := v1alpha1.BoxFeatureList([]any{
+ &plugins.Generic{Host: "node1", Value: 1.0},
+ &plugins.Generic{Host: "node2", Value: 0.5},
+ })
+ if err != nil {
+ t.Fatalf("failed to box features: %v", err)
+ }
+
+ knowledge := v1alpha1.Knowledge{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-knowledge",
+ },
+ Status: v1alpha1.KnowledgeStatus{
+ Conditions: []metav1.Condition{
+ {
+ Type: v1alpha1.KnowledgeConditionReady,
+ },
+ },
+ Raw: features,
+ RawLength: 2,
+ },
+ }
+ client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&knowledge).Build()
+ spec := v1alpha1.WeigherSpec{
+ Params: []v1alpha1.Parameter{
+ {Key: "knowledge", StringValue: &knowledge.Name},
+ },
+ }
+ weigher := &GenericWeigherStep{}
+ err = weigher.Init(t.Context(), client, spec)
+ if err != nil {
+ t.Fatalf("failed to initialize weigher: %v", err)
+ }
+ request := pods.PodPipelineRequest{
+ Nodes: []corev1.Node{
+ {
+ ObjectMeta: metav1.ObjectMeta{Name: "node1"},
+ },
+ {
+ ObjectMeta: metav1.ObjectMeta{Name: "node2"},
+ },
+ {
+ ObjectMeta: metav1.ObjectMeta{Name: "node3"},
+ },
+ },
+ Pod: corev1.Pod{
+ ObjectMeta: metav1.ObjectMeta{Name: "test-pod"},
+ },
+ }
+
+ actual, err := weigher.Run(t.Context(), nil, request)
+ if err != nil {
+ t.Fatalf("failed to run weigher: %v", err)
+ }
+ expected := map[string]float64{
+ "node1": 1.0,
+ "node2": 0.5,
+ "node3": 0.0,
+ }
+ for node, expectedWeight := range expected {
+ if actual.Activations[node] != expectedWeight {
+ t.Errorf("expected weight for node %s to be %f, got %f", node, expectedWeight, actual.Activations[node])
+ }
+ }
+}