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]) + } + } +}