From d52cb3ce784b0de275893e642e763991e5a6000a 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 c01ab9062..c64ff6909 100644
--- a/helm/library/cortex/templates/rbac/role.yaml
+++ b/helm/library/cortex/templates/rbac/role.yaml
@@ -51,4 +51,11 @@ rules:
- get
- patch
- update
+- apiGroups: [""]
+ resources:
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
{{- end -}}
From e528f66af6425d0bfd346541e6468a1e1abd5fbf 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_allowed_projects.go | 4 ++--
.../filters/filter_allowed_projects_test.go | 2 +-
.../nova/plugins/filters/filter_capabilities.go | 4 ++--
.../plugins/filters/filter_capabilities_test.go | 2 +-
.../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 | 4 ++--
.../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 +++---
.../nova/plugins/filters/filter_maintenance.go | 4 ++--
.../plugins/filters/filter_maintenance_test.go | 2 +-
.../plugins/filters/filter_packed_virtqueue.go | 4 ++--
.../filters/filter_packed_virtqueue_test.go | 2 +-
.../filters/filter_requested_destination.go | 3 ++-
.../filters/filter_requested_destination_test.go | 4 ++--
.../plugins/filters/filter_status_conditions.go | 4 ++--
.../filters/filter_status_conditions_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 +-
.../weighers/vmware_general_purpose_balancing.go | 6 +++---
.../vmware_general_purpose_balancing_test.go | 2 +-
.../plugins/weighers/vmware_hana_binpacking.go | 6 +++---
.../weighers/vmware_hana_binpacking_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 +-
79 files changed, 138 insertions(+), 124 deletions(-)
diff --git a/internal/scheduling/cinder/filter_weigher_pipeline_controller.go b/internal/scheduling/cinder/filter_weigher_pipeline_controller.go
index 0d8771081..c23acc064 100644
--- a/internal/scheduling/cinder/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/cinder/filter_weigher_pipeline_controller.go
@@ -129,7 +129,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 c60dbe090..0ce339bb6 100644
--- a/internal/scheduling/lib/filter_monitor.go
+++ b/internal/scheduling/lib/filter_monitor.go
@@ -38,6 +38,6 @@ func (fm *FilterMonitor[RequestType]) Init(ctx context.Context, client client.Cl
}
// 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 3e7738c40..46b561ad1 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
@@ -79,7 +79,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},
@@ -102,7 +102,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 ac7c4d1d7..986cfbf68 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
- 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 {
@@ -25,11 +25,11 @@ func (m *mockFilter[RequestType]) Init(ctx context.Context, client client.Client
}
return m.InitFunc(ctx, client, step)
}
-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 5fdecb848..cde547124 100644
--- a/internal/scheduling/lib/filter_validation.go
+++ b/internal/scheduling/lib/filter_validation.go
@@ -30,8 +30,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 bb90c9023..14142c259 100644
--- a/internal/scheduling/lib/filter_validation_test.go
+++ b/internal/scheduling/lib/filter_validation_test.go
@@ -151,7 +151,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
},
}
@@ -161,7 +161,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 188d7b657..1968b9c40 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.
@@ -132,6 +132,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) {
@@ -141,7 +142,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
@@ -160,6 +161,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 {
@@ -173,7 +175,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
@@ -243,7 +245,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 {
@@ -260,7 +262,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 := p.runFilters(traceLog, request)
+ filteredRequest := p.runFilters(ctx, traceLog, request)
traceLog.Info(
"scheduler: finished filters",
"remainingHosts", filteredRequest.GetHosts(),
@@ -271,7 +273,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(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 c10de1a6d..92c7d7195 100644
--- a/internal/scheduling/lib/filter_weigher_pipeline_step.go
+++ b/internal/scheduling/lib/filter_weigher_pipeline_step.go
@@ -29,7 +29,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 d69efb1f7..8a0298dc8 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 e777a2e33..78679dd29 100644
--- a/internal/scheduling/lib/weigher_monitor.go
+++ b/internal/scheduling/lib/weigher_monitor.go
@@ -38,6 +38,6 @@ func (wm *WeigherMonitor[RequestType]) Init(ctx context.Context, client client.C
}
// 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 1641454c9..95c5e39ee 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
@@ -79,7 +79,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},
@@ -102,7 +102,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 b1db3ec58..e714a24ee 100644
--- a/internal/scheduling/lib/weigher_test.go
+++ b/internal/scheduling/lib/weigher_test.go
@@ -18,7 +18,7 @@ import (
type mockWeigher[RequestType FilterWeigherPipelineRequest] struct {
InitFunc func(ctx context.Context, client client.Client, step v1alpha1.WeigherSpec) 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 {
@@ -27,11 +27,11 @@ func (m *mockWeigher[RequestType]) Init(ctx context.Context, client client.Clien
}
return m.InitFunc(ctx, client, step)
}
-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 3df01c264..f76138f49 100644
--- a/internal/scheduling/lib/weigher_validation.go
+++ b/internal/scheduling/lib/weigher_validation.go
@@ -30,8 +30,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 72157cb92..8ab34a9ea 100644
--- a/internal/scheduling/lib/weigher_validation_test.go
+++ b/internal/scheduling/lib/weigher_validation_test.go
@@ -83,7 +83,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,
@@ -101,7 +101,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)
}
@@ -118,7 +118,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,
@@ -135,7 +135,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 2b0c44f64..730c64610 100644
--- a/internal/scheduling/machines/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/machines/filter_weigher_pipeline_controller.go
@@ -152,7 +152,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 42c2e1661..11e071f77 100644
--- a/internal/scheduling/machines/filter_weigher_pipeline_controller_test.go
+++ b/internal/scheduling/machines/filter_weigher_pipeline_controller_test.go
@@ -547,7 +547,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 27c79e0e0..2fd2f9b53 100644
--- a/internal/scheduling/machines/plugins/filters/filter_noop.go
+++ b/internal/scheduling/machines/plugins/filters/filter_noop.go
@@ -27,7 +27,7 @@ func (f *NoopFilter) Init(ctx context.Context, client client.Client, filter v1al
// 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 3b63d64e0..34264ddb6 100644
--- a/internal/scheduling/manila/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/manila/filter_weigher_pipeline_controller.go
@@ -129,7 +129,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 9c10a57b2..05884c806 100644
--- a/internal/scheduling/nova/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/nova/filter_weigher_pipeline_controller.go
@@ -148,7 +148,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_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 a6c105d29..fc0eb5b95 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)
requestedCapabilities := request.Spec.Data.Flavor.Data.ExtraSpecs
if len(requestedCapabilities) == 0 {
@@ -76,7 +76,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 8999b3a88..dc0e0ce13 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)
}
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 62c059b10..9a633d1f8 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 fb9971a83..c42665f3a 100644
--- a/internal/scheduling/nova/plugins/filters/filter_external_customer_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_external_customer_test.go
@@ -354,7 +354,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 046a0c3f0..1f1811553 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
@@ -40,7 +40,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.
@@ -50,7 +50,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
}
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 be1382c9e..072b1fb6f 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 {
@@ -47,7 +47,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_maintenance.go b/internal/scheduling/nova/plugins/filters/filter_maintenance.go
index a5226d78c..6fd1d5161 100644
--- a/internal/scheduling/nova/plugins/filters/filter_maintenance.go
+++ b/internal/scheduling/nova/plugins/filters/filter_maintenance.go
@@ -17,11 +17,11 @@ type FilterMaintenanceStep struct {
}
// Check that the maintenance spec of the hypervisor doesn't prevent scheduling.
-func (s *FilterMaintenanceStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterMaintenanceStep) 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_maintenance_test.go b/internal/scheduling/nova/plugins/filters/filter_maintenance_test.go
index 14a496228..515f4b474 100644
--- a/internal/scheduling/nova/plugins/filters/filter_maintenance_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_maintenance_test.go
@@ -185,7 +185,7 @@ func TestFilterMaintenanceStep_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_packed_virtqueue.go b/internal/scheduling/nova/plugins/filters/filter_packed_virtqueue.go
index dac317e59..1ff9acc0b 100644
--- a/internal/scheduling/nova/plugins/filters/filter_packed_virtqueue.go
+++ b/internal/scheduling/nova/plugins/filters/filter_packed_virtqueue.go
@@ -18,7 +18,7 @@ type FilterPackedVirtqueueStep struct {
}
// If requested, only get hosts with packed virtqueues.
-func (s *FilterPackedVirtqueueStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *FilterPackedVirtqueueStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
// We don't care about the value.
_, reqInSpecs := request.Spec.Data.Flavor.Data.ExtraSpecs["hw:virtio_packed_ring"]
@@ -29,7 +29,7 @@ func (s *FilterPackedVirtqueueStep) 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_packed_virtqueue_test.go b/internal/scheduling/nova/plugins/filters/filter_packed_virtqueue_test.go
index 82b68da81..bc33f55e3 100644
--- a/internal/scheduling/nova/plugins/filters/filter_packed_virtqueue_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_packed_virtqueue_test.go
@@ -482,7 +482,7 @@ func TestFilterPackedVirtqueueStep_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_requested_destination.go b/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
index 0bab31085..a101fe61f 100644
--- a/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
+++ b/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
@@ -21,6 +21,7 @@ type FilterRequestedDestinationStep struct {
// accordingly. The requested destination can be a specific host, or
// an aggregate.
func (s *FilterRequestedDestinationStep) Run(
+ ctx context.Context,
traceLog *slog.Logger,
request api.ExternalSchedulerRequest,
) (*lib.FilterWeigherPipelineStepResult, error) {
@@ -38,7 +39,7 @@ func (s *FilterRequestedDestinationStep) 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_requested_destination_test.go b/internal/scheduling/nova/plugins/filters/filter_requested_destination_test.go
index dafbb5a56..35513f73c 100644
--- a/internal/scheduling/nova/plugins/filters/filter_requested_destination_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_requested_destination_test.go
@@ -501,7 +501,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 {
@@ -584,7 +584,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 e086f75f7..f6f92de77 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 90dd7bc3c..077fbb12f 100644
--- a/internal/scheduling/nova/plugins/filters/filter_status_conditions_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_status_conditions_test.go
@@ -286,7 +286,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/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 56b1abfbc..570f73e00 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
@@ -259,7 +259,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 fba1eb05b..3957428d1 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
@@ -234,7 +234,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 ee4e6206a..180c8fdf9 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
@@ -234,7 +234,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_general_purpose_balancing.go b/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing.go
index 0c7014d33..51b656fd3 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing.go
@@ -54,7 +54,7 @@ func (s *VMwareGeneralPurposeBalancingStep) Init(ctx context.Context, client cli
}
// Pack VMs on hosts based on their flavor.
-func (s *VMwareGeneralPurposeBalancingStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *VMwareGeneralPurposeBalancingStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
// Don't execute the step for non-hana flavors.
if strings.Contains(request.Spec.Data.Flavor.Data.Name, "hana") {
@@ -66,7 +66,7 @@ func (s *VMwareGeneralPurposeBalancingStep) Run(traceLog *slog.Logger, request a
hostUtilizationKnowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "host-utilization"},
hostUtilizationKnowledge,
); err != nil {
@@ -99,7 +99,7 @@ func (s *VMwareGeneralPurposeBalancingStep) Run(traceLog *slog.Logger, request a
// this step is only executed for VMware hosts.
hostCapabilitiesKnowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "host-capabilities"},
hostCapabilitiesKnowledge,
); err != nil {
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing_test.go b/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing_test.go
index 2d428b61c..c732247db 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing_test.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing_test.go
@@ -289,7 +289,7 @@ func TestVMwareGeneralPurposeBalancingStep_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_hana_binpacking.go b/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking.go
index 7c1b607cc..d93bc9edd 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking.go
@@ -54,7 +54,7 @@ func (s *VMwareHanaBinpackingStep) Init(ctx context.Context, client client.Clien
}
// Pack VMs on hosts based on their flavor.
-func (s *VMwareHanaBinpackingStep) Run(traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
+func (s *VMwareHanaBinpackingStep) Run(ctx context.Context, traceLog *slog.Logger, request api.ExternalSchedulerRequest) (*lib.FilterWeigherPipelineStepResult, error) {
result := s.IncludeAllHostsFromRequest(request)
// Don't execute the step for non-hana flavors.
if !strings.Contains(request.Spec.Data.Flavor.Data.Name, "hana") {
@@ -69,7 +69,7 @@ func (s *VMwareHanaBinpackingStep) Run(traceLog *slog.Logger, request api.Extern
// this step is only executed for VMware hosts.
hostCapabilitiesKnowledge := &v1alpha1.Knowledge{}
if err := s.Client.Get(
- context.Background(),
+ ctx,
client.ObjectKey{Name: "host-capabilities"},
hostCapabilitiesKnowledge,
); err != nil {
@@ -104,7 +104,7 @@ func (s *VMwareHanaBinpackingStep) Run(traceLog *slog.Logger, request api.Extern
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_hana_binpacking_test.go b/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking_test.go
index cb5216a92..7ed0afeed 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking_test.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking_test.go
@@ -236,7 +236,7 @@ func TestVMwareHanaBinpackingStep_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 28e10ff88..43b9e8db5 100644
--- a/internal/scheduling/pods/filter_weigher_pipeline_controller.go
+++ b/internal/scheduling/pods/filter_weigher_pipeline_controller.go
@@ -165,7 +165,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 2dd48d339..cd831e82b 100644
--- a/internal/scheduling/pods/filter_weigher_pipeline_controller_test.go
+++ b/internal/scheduling/pods/filter_weigher_pipeline_controller_test.go
@@ -523,7 +523,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 44bf7363c..a00056683 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_affinity.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_affinity.go
@@ -23,7 +23,7 @@ func (f *NodeAffinityFilter) Init(ctx context.Context, client client.Client, ste
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 7ad4ba71c..38d197386 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_affinity_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_affinity_test.go
@@ -401,7 +401,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 e0514feee..d985ac3cb 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_available.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_available.go
@@ -22,7 +22,7 @@ func (f *NodeAvailableFilter) Init(ctx context.Context, client client.Client, st
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 7c98decb6..f3ed29465 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_available_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_available_test.go
@@ -311,7 +311,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 692f55bc9..dbd1f3f42 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_capacity.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_capacity.go
@@ -23,7 +23,7 @@ func (f *NodeCapacityFilter) Init(ctx context.Context, client client.Client, ste
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 bf8760442..59be4be88 100644
--- a/internal/scheduling/pods/plugins/filters/filter_node_capacity_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_node_capacity_test.go
@@ -353,7 +353,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 1a4c898c0..874e08797 100644
--- a/internal/scheduling/pods/plugins/filters/filter_noop.go
+++ b/internal/scheduling/pods/plugins/filters/filter_noop.go
@@ -27,7 +27,7 @@ func (f *NoopFilter) Init(ctx context.Context, client client.Client, step v1alph
// 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 de5505ed1..1c4089956 100644
--- a/internal/scheduling/pods/plugins/filters/filter_noop_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_noop_test.go
@@ -87,7 +87,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 1325a8b14..c9eab8372 100644
--- a/internal/scheduling/pods/plugins/filters/filter_taint.go
+++ b/internal/scheduling/pods/plugins/filters/filter_taint.go
@@ -22,7 +22,7 @@ func (f *TaintFilter) Init(ctx context.Context, client client.Client, step v1alp
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 345982754..2ffbb6856 100644
--- a/internal/scheduling/pods/plugins/filters/filter_taint_test.go
+++ b/internal/scheduling/pods/plugins/filters/filter_taint_test.go
@@ -254,7 +254,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 c91ba509935f0eeaeea4e25b3ee95e9cfab17a5b 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 ++--
.../weighers/vmware_general_purpose_balancing.go | 6 +++---
.../plugins/weighers/vmware_hana_binpacking.go | 6 +++---
8 files changed, 25 insertions(+), 28 deletions(-)
diff --git a/internal/scheduling/lib/weigher.go b/internal/scheduling/lib/weigher.go
index ef3d213b3..6fb0db177 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"
)
@@ -34,16 +34,13 @@ func (s *BaseWeigher[RequestType, Opts]) Init(ctx context.Context, client client
}
// 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 e714a24ee..2336fe472 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"
)
@@ -106,7 +106,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
tests := []struct {
name string
knowledges []v1alpha1.Knowledge
- refs []corev1.ObjectReference
+ refs []types.NamespacedName
expectError bool
errorMsg string
}{
@@ -129,7 +129,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
},
},
},
- refs: []corev1.ObjectReference{
+ refs: []types.NamespacedName{
{Name: "knowledge1", Namespace: "default"},
},
expectError: false,
@@ -137,7 +137,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,
@@ -162,7 +162,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
},
},
},
- refs: []corev1.ObjectReference{
+ refs: []types.NamespacedName{
{Name: "knowledge1", Namespace: "default"},
},
expectError: true,
@@ -187,7 +187,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
},
},
},
- refs: []corev1.ObjectReference{
+ refs: []types.NamespacedName{
{Name: "knowledge1", Namespace: "default"},
},
expectError: true,
@@ -196,7 +196,7 @@ func TestBaseFilterWeigherPipelineStep_CheckKnowledges(t *testing.T) {
{
name: "empty knowledge list",
knowledges: []v1alpha1.Knowledge{},
- refs: []corev1.ObjectReference{},
+ refs: []types.NamespacedName{},
expectError: false,
},
}
@@ -235,7 +235,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_general_purpose_balancing.go b/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing.go
index 51b656fd3..5dcb9f514 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_general_purpose_balancing.go
@@ -13,7 +13,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,8 +45,8 @@ func (s *VMwareGeneralPurposeBalancingStep) Init(ctx context.Context, client cli
return err
}
if err := s.CheckKnowledges(ctx,
- corev1.ObjectReference{Name: "host-utilization"},
- corev1.ObjectReference{Name: "host-capabilities"},
+ types.NamespacedName{Name: "host-utilization"},
+ types.NamespacedName{Name: "host-capabilities"},
); err != nil {
return err
}
diff --git a/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking.go b/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking.go
index d93bc9edd..d93ee5331 100644
--- a/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking.go
+++ b/internal/scheduling/nova/plugins/weighers/vmware_hana_binpacking.go
@@ -13,7 +13,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,8 +45,8 @@ func (s *VMwareHanaBinpackingStep) Init(ctx context.Context, client client.Clien
return err
}
if err := s.CheckKnowledges(ctx,
- corev1.ObjectReference{Name: "host-utilization"},
- corev1.ObjectReference{Name: "host-capabilities"},
+ types.NamespacedName{Name: "host-utilization"},
+ types.NamespacedName{Name: "host-capabilities"},
); err != nil {
return err
}
From 9ce631cb610b7b8e73f54c51092d08bdb818bd35 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 cd4f63972..752c4fcb9 100644
--- a/internal/knowledge/extractor/controller.go
+++ b/internal/knowledge/extractor/controller.go
@@ -8,7 +8,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"
@@ -76,6 +75,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{
@@ -116,7 +116,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 {
@@ -160,7 +187,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 b9f84b188..1f8c3be97 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..75e2662fc 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 := "vrops-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 453a62751db8f24a4c970da20f234d381e7f3b25 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 ++++
.../knowledge/extractor/plugins/generic.go | 40 ++++
.../extractor/plugins/generic_test.go | 74 ++++++++
.../extractor/supported_extractors.go | 2 +
.../pods/plugins/filters/filter_generic.go | 73 ++++++++
.../plugins/filters/filter_generic_test.go | 176 +++++++++++++++++
.../pods/plugins/weighers/generic.go | 71 +++++++
.../pods/plugins/weighers/generic_test.go | 177 ++++++++++++++++++
9 files changed, 648 insertions(+)
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/generic.go b/internal/knowledge/extractor/plugins/generic.go
new file mode 100644
index 000000000..00c29bfa3
--- /dev/null
+++ b/internal/knowledge/extractor/plugins/generic.go
@@ -0,0 +1,40 @@
+// 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("TODO")
+ }
+ dsSpec := &d[0].Spec
+ if dsSpec.Type != v1alpha1.DatasourceTypePrometheus {
+ return nil, errors.New("TODO")
+ }
+ name := dsSpec.Prometheus.Alias
+ if name == "" {
+ return nil, errors.New("TODO")
+ }
+
+ query := "SELECT host, value FROM generic WHERE name = '" + name + "'"
+ return e.ExtractSQL(query)
+}
diff --git a/internal/knowledge/extractor/plugins/generic_test.go b/internal/knowledge/extractor/plugins/generic_test.go
new file mode 100644
index 000000000..9119099dd
--- /dev/null
+++ b/internal/knowledge/extractor/plugins/generic_test.go
@@ -0,0 +1,74 @@
+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 684697928..02e3e1530 100644
--- a/internal/knowledge/extractor/supported_extractors.go
+++ b/internal/knowledge/extractor/supported_extractors.go
@@ -25,4 +25,6 @@ var supportedExtractors = map[string]plugins.FeatureExtractor{
"sap_host_details_extractor": &compute.HostDetailsExtractor{},
"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..d4e6942f4
--- /dev/null
+++ b/internal/scheduling/pods/plugins/filters/filter_generic.go
@@ -0,0 +1,73 @@
+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..91e837f3d
--- /dev/null
+++ b/internal/scheduling/pods/plugins/filters/filter_generic_test.go
@@ -0,0 +1,176 @@
+package filters
+
+import (
+ "fmt"
+ "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"
+ "k8s.io/apimachinery/pkg/runtime"
+ "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: runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"knowledge": "%s"}`, 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: runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"knowledge": "%s"}`, 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..8af118e3d
--- /dev/null
+++ b/internal/scheduling/pods/plugins/weighers/generic.go
@@ -0,0 +1,71 @@
+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..cfcdadea6
--- /dev/null
+++ b/internal/scheduling/pods/plugins/weighers/generic_test.go
@@ -0,0 +1,177 @@
+package weighers
+
+import (
+ "fmt"
+ "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"
+ "k8s.io/apimachinery/pkg/runtime"
+ "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: runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"knowledge": "%s"}`, 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: runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"knowledge": "%s"}`, 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])
+ }
+ }
+}