This is an automated email from the ASF dual-hosted git repository.
wilfred-s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/yunikorn-k8shim.git
The following commit(s) were added to refs/heads/master by this push:
new 1e54dced [YUNIKORN-3300] Use feasible nodes returned from K8s (#1043)
1e54dced is described below
commit 1e54dced2d0433753a66dfb9c980f8cabad56290
Author: Manikandan R <[email protected]>
AuthorDate: Tue Sep 8 18:39:10 2026 +1000
[YUNIKORN-3300] Use feasible nodes returned from K8s (#1043)
Made changes in preemption flow similar to the scheduling cycle flow in
using prefilter and filter plugins. Use newly introduced SI API's to receive
args and send respones of prefilter predicates API.
Cache cycle state only when prefilter succeeds and delete as and when filter
checks are over.
Introduced Mocks for PreFilter & Filter Plugins. Used those mocks to improve
test coverage for PreFilter and Filter predicate checks in PredicateManager.
Delete cycle state even during preemption predicate checks and added tests.
Closes: #1043
Signed-off-by: Wilfred Spiegelenburg <[email protected]>
---
go.mod | 98 ++---
go.sum | 164 ++++----
pkg/cache/context.go | 72 +++-
pkg/cache/external/scheduler_cache.go | 72 +++-
pkg/cache/scheduler_callback.go | 9 +
pkg/cache/scheduler_callback_test.go | 95 ++++-
pkg/cache/task.go | 7 +
pkg/plugin/predicates/predicate_manager.go | 154 ++++----
pkg/plugin/predicates/predicate_manager_test.go | 499 ++++++++++++++++++++++--
9 files changed, 884 insertions(+), 286 deletions(-)
diff --git a/go.mod b/go.mod
index 019eb8ab..7a20b685 100644
--- a/go.mod
+++ b/go.mod
@@ -26,82 +26,91 @@ require (
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/looplab/fsm v1.0.3
- github.com/onsi/ginkgo/v2 v2.28.1
- github.com/onsi/gomega v1.40.0
+ github.com/onsi/ginkgo/v2 v2.32.1
+ github.com/onsi/gomega v1.42.1
github.com/prometheus/client_golang v1.24.1
github.com/sasha-s/go-deadlock v0.3.9
go.uber.org/zap v1.28.0
go.yaml.in/yaml/v3 v3.0.5
gotest.tools/v3 v3.5.2
- k8s.io/api v0.36.1
- k8s.io/apimachinery v0.36.1
- k8s.io/apiserver v0.36.1
- k8s.io/cli-runtime v0.36.1
- k8s.io/client-go v0.36.1
- k8s.io/component-base v0.36.1
- k8s.io/component-helpers v0.36.1
- k8s.io/dynamic-resource-allocation v0.36.1
+ k8s.io/api v0.36.3
+ k8s.io/apimachinery v0.36.3
+ k8s.io/apiserver v0.36.3
+ k8s.io/cli-runtime v0.36.3
+ k8s.io/client-go v1.5.2
+ k8s.io/component-base v0.36.3
+ k8s.io/component-helpers v0.36.3
+ k8s.io/dynamic-resource-allocation v0.36.3
k8s.io/klog/v2 v2.140.0
- k8s.io/kube-scheduler v0.36.1
- k8s.io/kubernetes v1.36.1
- k8s.io/streaming v0.36.1
+ k8s.io/kube-scheduler v0.36.3
+ k8s.io/kubernetes v1.36.3
+ k8s.io/streaming v0.36.3
)
require (
cel.dev/expr v0.25.2 // indirect
- github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 //
indirect
+ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c //
indirect
github.com/Azure/go-ntlmssp v0.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.4.0 // indirect
+ github.com/Masterminds/semver/v3 v3.5.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc //
indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
- github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.9.2 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
- github.com/go-errors/errors v1.4.2 // indirect
+ github.com/go-errors/errors v1.5.1 // indirect
github.com/go-ldap/ldap/v3 v3.4.14 // indirect
- github.com/go-logr/logr v1.4.3 // indirect
- github.com/go-openapi/jsonpointer v0.21.0 // indirect
- github.com/go-openapi/jsonreference v0.20.2 // indirect
- github.com/go-openapi/swag v0.23.0 // indirect
+ github.com/go-logr/logr v1.4.4 // indirect
+ github.com/go-openapi/jsonpointer v1.0.0 // indirect
+ github.com/go-openapi/jsonreference v1.0.0 // indirect
+ github.com/go-openapi/swag v0.28.0 // indirect
+ github.com/go-openapi/swag/cmdutils v0.28.0 // indirect
+ github.com/go-openapi/swag/conv v0.28.0 // indirect
+ github.com/go-openapi/swag/fileutils v0.28.0 // indirect
+ github.com/go-openapi/swag/jsonutils v0.28.0 // indirect
+ github.com/go-openapi/swag/loading v0.28.0 // indirect
+ github.com/go-openapi/swag/mangling v0.28.0 // indirect
+ github.com/go-openapi/swag/netutils v0.28.0 // indirect
+ github.com/go-openapi/swag/pools v0.28.0 // indirect
+ github.com/go-openapi/swag/stringutils v0.28.0 // indirect
+ github.com/go-openapi/swag/typeutils v0.28.0 // indirect
+ github.com/go-openapi/swag/yamlutils v0.28.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
- github.com/google/cel-go v0.29.0 // indirect
- github.com/google/gnostic-models v0.7.0 // indirect
- github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect
+ github.com/google/cel-go v0.31.0 // indirect
+ github.com/google/gnostic-models v0.7.1 // indirect
+ github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 //
indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de //
indirect
- github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/spdystream v0.5.1 // indirect
- github.com/moby/term v0.5.0 // indirect
+ github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd //
indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee //
indirect
github.com/monochromegane/go-gitignore
v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 //
indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
- github.com/petermattis/goid v0.0.0-20260725062400-500c67a39b75 //
indirect
+ github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b //
indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 //
indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.70.1 // indirect
github.com/prometheus/procfs v0.21.1 // indirect
github.com/spf13/cobra v1.10.2 // indirect
- github.com/spf13/pflag v1.0.9 // indirect
+ github.com/spf13/pflag v1.0.10 // indirect
github.com/tidwall/btree v1.8.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
- go.opentelemetry.io/otel v1.44.0 // indirect
- go.opentelemetry.io/otel/trace v1.44.0 // indirect
+ go.opentelemetry.io/otel v1.45.0 // indirect
+ go.opentelemetry.io/otel/trace v1.45.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
golang.org/x/crypto v0.56.0 // indirect
- golang.org/x/exp v0.0.0-20260727155853-b88d891fe743 // indirect
- golang.org/x/mod v0.38.0 // indirect
+ golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297 // indirect
+ golang.org/x/mod v0.40.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.22.0 // indirect
@@ -109,25 +118,24 @@ require (
golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/time v0.15.0 // indirect
- golang.org/x/tools v0.48.0 // indirect
- google.golang.org/genproto/googleapis/api
v0.0.0-20260526163538-3dc84a4a5aaa // indirect
- google.golang.org/genproto/googleapis/rpc
v0.0.0-20260803160001-6ac0973c030d // indirect
+ golang.org/x/tools v0.49.0 // indirect
+ google.golang.org/genproto/googleapis/api
v0.0.0-20260810153831-ec0a7760b754 // indirect
+ google.golang.org/genproto/googleapis/rpc
v0.0.0-20260810153831-ec0a7760b754 // indirect
google.golang.org/grpc v1.83.1 // indirect
- google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af //
indirect
+ google.golang.org/protobuf v1.36.12 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/apiextensions-apiserver v0.0.0 // indirect
- k8s.io/cloud-provider v0.34.2 // indirect
- k8s.io/controller-manager v0.36.1 // indirect
- k8s.io/csi-translation-lib v0.34.2 // indirect
- k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
- k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 // indirect
+ k8s.io/apiextensions-apiserver v0.36.3 // indirect
+ k8s.io/cloud-provider v0.36.3 // indirect
+ k8s.io/controller-manager v0.36.3 // indirect
+ k8s.io/csi-translation-lib v0.36.3 // indirect
+ k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect
+ k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/kustomize/api v0.21.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.21.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
- sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
+ sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/go.sum b/go.sum
index 2abddadb..43150054 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,11 @@
cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs=
cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
-github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161
h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
-github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod
h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c
h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod
h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-ntlmssp v0.1.1
h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw=
github.com/Azure/go-ntlmssp v0.1.1/go.mod
h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
-github.com/Masterminds/semver/v3 v3.4.0
h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
-github.com/Masterminds/semver/v3 v3.4.0/go.mod
h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/Masterminds/semver/v3 v3.5.0
h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
+github.com/Masterminds/semver/v3 v3.5.0/go.mod
h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e
h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod
h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/antlr4-go/antlr/v4 v4.13.1
h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
@@ -23,7 +23,6 @@ github.com/blang/semver/v4 v4.0.0/go.mod
h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2y
github.com/cespare/xxhash/v2 v2.3.0
h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod
h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod
h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
-github.com/creack/pty v1.1.9/go.mod
h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod
h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -34,8 +33,8 @@ github.com/distribution/reference v0.6.0
h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod
h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/emicklei/go-restful/v3 v3.13.0
h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod
h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/fxamacker/cbor/v2 v2.9.0
h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
-github.com/fxamacker/cbor/v2 v2.9.0/go.mod
h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/fxamacker/cbor/v2 v2.9.2
h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78=
+github.com/fxamacker/cbor/v2 v2.9.2/go.mod
h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gkampitakis/ciinfo v0.3.2
h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
github.com/gkampitakis/ciinfo v0.3.2/go.mod
h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2
h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
@@ -44,22 +43,48 @@ github.com/gkampitakis/go-snaps v0.5.15
h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01
github.com/gkampitakis/go-snaps v0.5.15/go.mod
h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/go-asn1-ber/asn1-ber v1.5.8
h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod
h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-errors/errors v1.4.2
h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
-github.com/go-errors/errors v1.4.2/go.mod
h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-errors/errors v1.5.1
h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
+github.com/go-errors/errors v1.5.1/go.mod
h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-ldap/ldap/v3 v3.4.14
h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
github.com/go-ldap/ldap/v3 v3.4.14/go.mod
h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
-github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
-github.com/go-logr/logr v1.4.3/go.mod
h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
+github.com/go-logr/logr v1.4.4/go.mod
h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod
h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-openapi/jsonpointer v0.19.6/go.mod
h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
-github.com/go-openapi/jsonpointer v0.21.0
h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
-github.com/go-openapi/jsonpointer v0.21.0/go.mod
h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
-github.com/go-openapi/jsonreference v0.20.2
h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
-github.com/go-openapi/jsonreference v0.20.2/go.mod
h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
-github.com/go-openapi/swag v0.22.3/go.mod
h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
-github.com/go-openapi/swag v0.23.0
h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
-github.com/go-openapi/swag v0.23.0/go.mod
h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-openapi/jsonpointer v1.0.0
h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s=
+github.com/go-openapi/jsonpointer v1.0.0/go.mod
h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y=
+github.com/go-openapi/jsonreference v1.0.0
h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY=
+github.com/go-openapi/jsonreference v1.0.0/go.mod
h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0=
+github.com/go-openapi/swag v0.28.0
h1:xkgbOSKj6DZziNpyqRRAOt3GJGtgjgsd2RoyT30VWuw=
+github.com/go-openapi/swag v0.28.0/go.mod
h1:4qYnT3Cqr1p1VknOdPo70evN4rgQnAg6jwApHyxSGIg=
+github.com/go-openapi/swag/cmdutils v0.28.0
h1:7TOeNtkYru1SG8Y34tDh9WBbLsMqGnptuxWiHREPZ4Q=
+github.com/go-openapi/swag/cmdutils v0.28.0/go.mod
h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM=
+github.com/go-openapi/swag/conv v0.28.0
h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8=
+github.com/go-openapi/swag/conv v0.28.0/go.mod
h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k=
+github.com/go-openapi/swag/fileutils v0.28.0
h1:Z04XWQD7R8Eq+7GnOrjovBxPPmZzsS4gt2H2GPGIViU=
+github.com/go-openapi/swag/fileutils v0.28.0/go.mod
h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8=
+github.com/go-openapi/swag/jsonutils v0.28.0
h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4=
+github.com/go-openapi/swag/jsonutils v0.28.0/go.mod
h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0
h1:qV+VVUAx5Oro8WjVWpZeql7YReTKhT4smR4zhcOQZr0=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0/go.mod
h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
+github.com/go-openapi/swag/loading v0.28.0
h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU=
+github.com/go-openapi/swag/loading v0.28.0/go.mod
h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k=
+github.com/go-openapi/swag/mangling v0.28.0
h1:pH8eyeNO9SLYsTMWJrurnNfKmDa28XrlA+HePVD53VM=
+github.com/go-openapi/swag/mangling v0.28.0/go.mod
h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w=
+github.com/go-openapi/swag/netutils v0.28.0
h1:YXN6TALEi2pzts8/8GNm6T61HTAZsieukGZidap989k=
+github.com/go-openapi/swag/netutils v0.28.0/go.mod
h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ=
+github.com/go-openapi/swag/pools v0.28.0
h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU=
+github.com/go-openapi/swag/pools v0.28.0/go.mod
h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
+github.com/go-openapi/swag/stringutils v0.28.0
h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU=
+github.com/go-openapi/swag/stringutils v0.28.0/go.mod
h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
+github.com/go-openapi/swag/typeutils v0.28.0
h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY=
+github.com/go-openapi/swag/typeutils v0.28.0/go.mod
h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
+github.com/go-openapi/swag/yamlutils v0.28.0
h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY=
+github.com/go-openapi/swag/yamlutils v0.28.0/go.mod
h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs=
+github.com/go-openapi/testify/enable/yaml/v2 v2.6.0
h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
+github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod
h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
+github.com/go-openapi/testify/v2 v2.6.0
h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
+github.com/go-openapi/testify/v2 v2.6.0/go.mod
h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
github.com/go-task/slim-sprig/v3 v3.0.0
h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod
h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/goccy/go-yaml v1.18.0
h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
@@ -68,15 +93,15 @@ github.com/golang/protobuf v1.5.4
h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod
h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod
h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/google/cel-go v0.29.0
h1:fEG+Ja3YRwNOqnQxTyJwoByAUAvTuxUGiro/jhrm4F4=
-github.com/google/cel-go v0.29.0/go.mod
h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
-github.com/google/gnostic-models v0.7.0
h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
-github.com/google/gnostic-models v0.7.0/go.mod
h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/cel-go v0.31.0
h1:H0bhpFTqOvmHrBGrWKp7ZlhBm5Hh8PYUEXnwxT1LL7A=
+github.com/google/cel-go v0.31.0/go.mod
h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
+github.com/google/gnostic-models v0.7.1
h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
+github.com/google/gnostic-models v0.7.1/go.mod
h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83
h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
-github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod
h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
+github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3
h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo=
+github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod
h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
@@ -97,8 +122,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4
h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod
h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3
h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod
h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
-github.com/josharian/intern v1.0.0
h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
-github.com/josharian/intern v1.0.0/go.mod
h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/joshdk/go-junit v1.0.0
h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
github.com/joshdk/go-junit v1.0.0/go.mod
h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
github.com/json-iterator/go v1.1.12
h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -107,11 +130,8 @@ github.com/julienschmidt/httprouter v1.3.0
h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d
github.com/julienschmidt/httprouter v1.3.0/go.mod
h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/klauspost/compress v1.19.1
h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
github.com/klauspost/compress v1.19.1/go.mod
h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
-github.com/kr/pretty v0.2.1/go.mod
h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod
h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod
h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod
h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0
h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
@@ -120,16 +140,14 @@ github.com/liggitt/tabwriter
v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod
h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/looplab/fsm v1.0.3 h1:qtxBsa2onOs0qFOtkqwf5zE0uP0+Te+wlIvXctPKpcw=
github.com/looplab/fsm v1.0.3/go.mod
h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4=
-github.com/mailru/easyjson v0.7.7
h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod
h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maruel/natural v1.1.1
h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod
h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/mfridman/tparse v0.18.0
h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
github.com/mfridman/tparse v0.18.0/go.mod
h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
github.com/moby/spdystream v0.5.1
h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y=
github.com/moby/spdystream v0.5.1/go.mod
h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
-github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
-github.com/moby/term v0.5.0/go.mod
h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
+github.com/moby/term v0.5.2/go.mod
h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -140,17 +158,17 @@ github.com/monochromegane/go-gitignore
v0.0.0-20200626010858-205db1a8cc00 h1:n6/
github.com/monochromegane/go-gitignore
v0.0.0-20200626010858-205db1a8cc00/go.mod
h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod
h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/onsi/ginkgo/v2 v2.28.1
h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
-github.com/onsi/ginkgo/v2 v2.28.1/go.mod
h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
-github.com/onsi/gomega v1.40.0 h1:Vtol0e1MghCD2ZVIilPDIg44XSL9l2QAn8ZNaljWcJc=
-github.com/onsi/gomega v1.40.0/go.mod
h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
+github.com/onsi/ginkgo/v2 v2.32.1
h1:6tlvcDm/3sE8lGJbZ4+d4mO3RLy24/tQWOFzVSQNIfw=
+github.com/onsi/ginkgo/v2 v2.32.1/go.mod
h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
+github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
+github.com/onsi/gomega v1.42.1/go.mod
h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
github.com/opencontainers/go-digest v1.0.0
h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod
h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/peterbourgon/diskv v2.0.1+incompatible
h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod
h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod
h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
-github.com/petermattis/goid v0.0.0-20260725062400-500c67a39b75
h1:VmZ6mKVkxavKEhEy4ZYyV7BwBYBFBP0TwIqmLk84fpU=
-github.com/petermattis/goid v0.0.0-20260725062400-500c67a39b75/go.mod
h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
+github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b
h1:sS7HLzwS+dO+gxATgQfeZDEdUZe2pKAB3nGoUwP5zU0=
+github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b/go.mod
h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pmezard/go-difflib v1.0.0/go.mod
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -173,18 +191,14 @@ github.com/sergi/go-diff v1.4.0
h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod
h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod
h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
-github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod
h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod
h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod
h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod
h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod
h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1
h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod
h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA=
@@ -203,16 +217,16 @@ github.com/xlab/treeprint v1.2.0
h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod
h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
go.opentelemetry.io/auto/sdk v1.2.1
h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod
h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/otel v1.44.0
h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
-go.opentelemetry.io/otel v1.44.0/go.mod
h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
-go.opentelemetry.io/otel/metric v1.44.0
h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
-go.opentelemetry.io/otel/metric v1.44.0/go.mod
h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
+go.opentelemetry.io/otel v1.45.0
h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
+go.opentelemetry.io/otel v1.45.0/go.mod
h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
+go.opentelemetry.io/otel/metric v1.45.0
h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
+go.opentelemetry.io/otel/metric v1.45.0/go.mod
h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/sdk v1.44.0
h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod
h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/sdk/metric v1.44.0
h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod
h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
-go.opentelemetry.io/otel/trace v1.44.0
h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
-go.opentelemetry.io/otel/trace v1.44.0/go.mod
h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
+go.opentelemetry.io/otel/trace v1.45.0
h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
+go.opentelemetry.io/otel/trace v1.45.0/go.mod
h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod
h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -226,10 +240,10 @@ go.yaml.in/yaml/v3 v3.0.5
h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod
h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod
h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
-golang.org/x/exp v0.0.0-20260727155853-b88d891fe743
h1:ex206bKw+v3K0dm3andkrIF+ijyQKJG1pLgwQ2PYdQM=
-golang.org/x/exp v0.0.0-20260727155853-b88d891fe743/go.mod
h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
-golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
-golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
+golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297
h1:YXnL44eJ77R+ji4/ooy8UsXIhz+lbi2Qgdlc8iRN0gY=
+golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297/go.mod
h1:Mkmymgv+uMpSQ/XxJ/7GpdrdYoqm3u72jEbpCLiJmNk=
+golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
+golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
@@ -245,21 +259,19 @@ golang.org/x/text v0.41.0
h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod
h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod
h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
-golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
-golang.org/x/tools v0.48.0/go.mod
h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
+golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
+golang.org/x/tools v0.49.0/go.mod
h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod
h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
-google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa
h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
-google.golang.org/genproto/googleapis/api
v0.0.0-20260526163538-3dc84a4a5aaa/go.mod
h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d
h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0=
-google.golang.org/genproto/googleapis/rpc
v0.0.0-20260803160001-6ac0973c030d/go.mod
h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/genproto/googleapis/api v0.0.0-20260810153831-ec0a7760b754
h1:dWeMvEJ3JhYgqSCAHUZZJgMUyfniiiCvDc72x5EqJP0=
+google.golang.org/genproto/googleapis/api
v0.0.0-20260810153831-ec0a7760b754/go.mod
h1:q/3oV3jAi5vwelxsVAprMBC8BcM2zmNe+IjRGd+9/ks=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260810153831-ec0a7760b754
h1:k5CJw9e5ONCcA/u0webKt092npXuY+KeGh3Q8NAVf0g=
+google.golang.org/genproto/googleapis/rpc
v0.0.0-20260810153831-ec0a7760b754/go.mod
h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y=
google.golang.org/grpc v1.83.1/go.mod
h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
-google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af
h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
-google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+google.golang.org/protobuf v1.36.12
h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
+google.golang.org/protobuf v1.36.12/go.mod
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod
h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.13.0
h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod
h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
@@ -295,18 +307,18 @@ k8s.io/dynamic-resource-allocation v0.36.1
h1:nMdVWmGuL+juHRvuBWGTngH2OYUmokwgRf
k8s.io/dynamic-resource-allocation v0.36.1/go.mod
h1:3dFGuVbN4Ui5M4EaXxFM+AktFQPKqiBAtyYb8zi3zw4=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
-k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a
h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
-k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod
h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
+k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad
h1:oXImqH8mQNk7PmvzKhmN3ddJoY6OnyM225MXwGHPm0A=
+k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad/go.mod
h1:0/mqHCVhlumdJ3BhCfnjSZQE037nAhNodh1/hK0T8/I=
k8s.io/kube-scheduler v0.36.1 h1:khJErGq9+JaHniVCxAulvQ4WvYxPj9/95bjJS0bXnjA=
k8s.io/kube-scheduler v0.36.1/go.mod
h1:iPnJPkET29aL/Ox0qupytdKosDz0qPj7xGH4MM+dz14=
k8s.io/kubelet v0.36.1 h1:FcHiG9wv92xerRPNxztuhYWqwS4IilOQNPxTPQewYgo=
k8s.io/kubelet v0.36.1/go.mod h1:e6IeoCwqc2TbneCKu6P8HjmWLi7U6SOh3Pocs32iGFM=
-k8s.io/kubernetes v1.36.1 h1:Mt7NKigaZ2KmOmCLhX81lGlH9JU5wjXnYhXnxAun9XA=
-k8s.io/kubernetes v1.36.1/go.mod
h1:MLdeJ3qw2CWH9BFml5GvptxQVQckz54fJOZ/WuixpFE=
-k8s.io/streaming v0.36.1 h1:L+K68n4Gg940BGNNYtUBvL1WTLL0YnKT3s+P1MNAmR4=
-k8s.io/streaming v0.36.1/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
-k8s.io/utils v0.0.0-20260507154919-ff6756f316d2
h1:wU4tMEhLGgIbLvXQb1cfN+EcM0wf7zC6CPF+C79jroc=
-k8s.io/utils v0.0.0-20260507154919-ff6756f316d2/go.mod
h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+k8s.io/kubernetes v1.36.3 h1:qDQdoMiluAE2Eab6Fa52YV+WjiGz9mZFFoagEA6cI+o=
+k8s.io/kubernetes v1.36.3/go.mod
h1:6oChkQeI7Yf6lV9lFpSdRzODdbY/ECp/4zUeBk8ONaw=
+k8s.io/streaming v0.36.3 h1:9rAaqBk0C0Pc7+/fqGekj07NV+/Xrew58p647A0JT8w=
+k8s.io/streaming v0.36.3/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
+k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3
h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE=
+k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod
h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730
h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod
h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kustomize/api v0.21.1
h1:lzqbzvz2CSvsjIUZUBNFKtIMsEw7hVLJp0JeSIVmuJs=
@@ -315,7 +327,7 @@ sigs.k8s.io/kustomize/kyaml v0.21.1
h1:IVlbmhC076nf6foyL6Taw4BkrLuEsXUXNpsE+ScX7
sigs.k8s.io/kustomize/kyaml v0.21.1/go.mod
h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod
h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
-sigs.k8s.io/structured-merge-diff/v6 v6.3.2
h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
-sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod
h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/structured-merge-diff/v6 v6.4.2
h1:qdOxHwrl2Kaag1aQEarlYcOA9vSyGCp3CIki3aW8c4Q=
+sigs.k8s.io/structured-merge-diff/v6 v6.4.2/go.mod
h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/pkg/cache/context.go b/pkg/cache/context.go
index 68e54fec..e0878250 100644
--- a/pkg/cache/context.go
+++ b/pkg/cache/context.go
@@ -41,6 +41,7 @@ import (
"k8s.io/klog/v2"
fwk "k8s.io/kube-scheduler/framework"
"k8s.io/kubernetes/pkg/features"
+ "k8s.io/kubernetes/pkg/scheduler/framework/plugins"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/dynamicresources"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumebinding"
"k8s.io/kubernetes/pkg/scheduler/util/assumecache"
@@ -64,8 +65,9 @@ import (
const registerNodeContextHandler = "RegisterNodeContextHandler"
var (
- ErrorPodNotFound = errors.New("predicates were not run because pod was
not found in cache")
- ErrorNodeNotFound = errors.New("predicates were not run because node
was not found in cache")
+ ErrorPodNotFound = errors.New("predicates were not run because
pod was not found in cache")
+ ErrorNodeNotFound = errors.New("predicates were not run because
node was not found in cache")
+ ErrorCycleStateNotFound = errors.New("predicates were not run because
pod's cycle state was not found in cache")
)
// context maintains scheduling state, like apps and apps' tasks.
@@ -127,7 +129,12 @@ func NewContextWithBootstrapConfigMaps(apis
client.APIProvider, bootstrapConfigM
}
sharedDRAManager =
dynamicresources.NewDRAManager(context.TODO(), resourceClaimCache,
resourceSliceTracker, informerFactory)
}
- ctx.predManager =
predicates.NewPredicateManager(support.NewFrameworkHandle(sharedLister,
informerFactory, clientSet, csiManager, sharedDRAManager))
+
+ config, err := predicates.DefaultConfig()
+ if err != nil {
+ log.Log(log.ShimClient).Error("unable to create the predicates
config", zap.Error(err))
+ }
+ ctx.predManager =
predicates.NewPredicateManager(support.NewFrameworkHandle(sharedLister,
informerFactory, clientSet, csiManager, sharedDRAManager),
plugins.NewInTreeRegistry(), config)
return ctx
}
@@ -690,6 +697,38 @@ func (ctx *Context) EventsToRegister(queueingHintFn
fwk.QueueingHintFn) []fwk.Cl
return ctx.predManager.EventsToRegister(queueingHintFn)
}
+// PreFilter evaluates given prefilter based predicates based on current
context
+func (ctx *Context) PreFilter(name string, allocate bool)
*si.PreFilterPredicatesResponse {
+ ctx.lock.RLock()
+ defer ctx.lock.RUnlock()
+ pod := ctx.schedulerCache.GetPod(name)
+ if pod == nil {
+ log.Log(log.ShimContext).Error("failed running PreFilter
plugin",
+ zap.String("pod", name),
+ zap.Error(ErrorPodNotFound))
+ return &si.PreFilterPredicatesResponse{
+ FeasibleNodes: make(map[string]*si.Empty),
+ Success: false,
+ }
+ }
+ // if pod exists in cache, try to run predicates
+ // need to lock cache here as predicates need a stable view into the
cache
+ ctx.schedulerCache.LockForReads()
+ feasibleNodes, cycleState, err := ctx.predManager.PreFilter(pod,
allocate)
+ ctx.schedulerCache.UnlockForReads()
+ if err == nil {
+ ctx.schedulerCache.StoreCycleState(pod, cycleState)
+ return &si.PreFilterPredicatesResponse{
+ FeasibleNodes: feasibleNodes,
+ Success: true,
+ }
+ }
+ return &si.PreFilterPredicatesResponse{
+ FeasibleNodes: make(map[string]*si.Empty),
+ Success: false,
+ }
+}
+
// IsPodFitNode evaluates given predicates based on current context
func (ctx *Context) IsPodFitNode(name, node string, allocate bool) error {
ctx.lock.RLock()
@@ -706,7 +745,11 @@ func (ctx *Context) IsPodFitNode(name, node string,
allocate bool) error {
// need to lock cache here as predicates need a stable view into the
cache
ctx.schedulerCache.LockForReads()
defer ctx.schedulerCache.UnlockForReads()
- plugin, err := ctx.predManager.Predicates(pod, targetNode, allocate)
+ cycleState := ctx.schedulerCache.GetCycleState(pod)
+ if cycleState == nil {
+ return ErrorCycleStateNotFound
+ }
+ plugin, err := ctx.predManager.Filter(pod, targetNode, cycleState,
allocate)
if err != nil {
err = errors.Join(fmt.Errorf("failed plugin: '%s'", plugin),
err)
}
@@ -722,17 +765,18 @@ func (ctx *Context) IsPodFitNodeViaPreemption(name, node
string, allocations []s
// need to lock cache here as predicates need a stable
view into the cache
ctx.schedulerCache.LockForReads()
defer ctx.schedulerCache.UnlockForReads()
+ if cycleState := ctx.schedulerCache.GetCycleState(pod);
cycleState != nil {
+ // look up each victim in the scheduler cache
+ victims := make([]*v1.Pod, len(allocations))
+ for index, uid := range allocations {
+ victim :=
ctx.schedulerCache.GetPodNoLock(uid)
+ victims[index] = victim
+ }
- // look up each victim in the scheduler cache
- victims := make([]*v1.Pod, len(allocations))
- for index, uid := range allocations {
- victim := ctx.schedulerCache.GetPodNoLock(uid)
- victims[index] = victim
- }
-
- // check predicates for a match
- if index := ctx.predManager.PreemptionPredicates(pod,
targetNode, victims, startIndex); index != -1 {
- return index, true
+ // check predicates for a match
+ if index :=
ctx.predManager.PreemptionFilter(pod, targetNode, cycleState, victims,
startIndex); index != -1 {
+ return index, true
+ }
}
}
}
diff --git a/pkg/cache/external/scheduler_cache.go
b/pkg/cache/external/scheduler_cache.go
index 31cea350..9b8ab4a4 100644
--- a/pkg/cache/external/scheduler_cache.go
+++ b/pkg/cache/external/scheduler_cache.go
@@ -41,16 +41,17 @@ import (
// Nodes are cached in the form of de-scheduler nodeInfo. Instead of
re-creating all nodes info from scratch,
// we replicate nodes info from de-scheduler, in order to re-use predicates
functions.
type SchedulerCache struct {
- nodesMap map[string]*framework.NodeInfo // node name to NodeInfo map
- podsMap map[string]*v1.Pod
- pcMap map[string]*schedulingv1.PriorityClass
- assignedPods map[string]string // map of pods to the node they are
currently assigned to
- assumedPods map[string]bool // map of assumed pods, value indicates
if pod volumes are all bound
- orphanedPods map[string]*v1.Pod // map of orphaned pods, keyed by pod
UID
- pvcRefCounts map[string]map[string]int
- lock locking.RWMutex
- clients *client.Clients // client APIs
- klogger klog.Logger
+ nodesMap map[string]*framework.NodeInfo // node name to NodeInfo
map
+ podsMap map[string]*v1.Pod
+ pcMap map[string]*schedulingv1.PriorityClass
+ assignedPods map[string]string // map of pods to the node they are
currently assigned to
+ assumedPods map[string]bool // map of assumed pods, value
indicates if pod volumes are all bound
+ orphanedPods map[string]*v1.Pod // map of orphaned pods, keyed by pod
UID
+ pvcRefCounts map[string]map[string]int
+ podsCycleState map[string]*framework.CycleState // map of pods to the
cycle state
+ lock locking.RWMutex
+ clients *client.Clients // client APIs
+ klogger klog.Logger
// cached data, re-calculated on demand from nodesMap
nodesInfo []fwk.NodeInfo
@@ -60,15 +61,16 @@ type SchedulerCache struct {
func NewSchedulerCache(clients *client.Clients) *SchedulerCache {
cache := &SchedulerCache{
- nodesMap: make(map[string]*framework.NodeInfo),
- podsMap: make(map[string]*v1.Pod),
- pcMap: make(map[string]*schedulingv1.PriorityClass),
- assignedPods: make(map[string]string),
- assumedPods: make(map[string]bool),
- orphanedPods: make(map[string]*v1.Pod),
- pvcRefCounts: make(map[string]map[string]int),
- clients: clients,
- klogger: klog.NewKlogr(),
+ nodesMap: make(map[string]*framework.NodeInfo),
+ podsMap: make(map[string]*v1.Pod),
+ pcMap: make(map[string]*schedulingv1.PriorityClass),
+ assignedPods: make(map[string]string),
+ assumedPods: make(map[string]bool),
+ orphanedPods: make(map[string]*v1.Pod),
+ pvcRefCounts: make(map[string]map[string]int),
+ podsCycleState: make(map[string]*framework.CycleState),
+ clients: clients,
+ klogger: klog.NewKlogr(),
}
return cache
}
@@ -135,6 +137,14 @@ func (cache *SchedulerCache) UnlockForReads() {
cache.lock.RUnlock()
}
+func (cache *SchedulerCache) LockForWrites() {
+ cache.lock.Lock()
+}
+
+func (cache *SchedulerCache) UnlockForWrites() {
+ cache.lock.Unlock()
+}
+
func (cache *SchedulerCache) GetNode(name string) *framework.NodeInfo {
cache.lock.RLock()
defer cache.lock.RUnlock()
@@ -386,6 +396,7 @@ func (cache *SchedulerCache) updatePod(pod *v1.Pod) bool {
delete(cache.assignedPods, key)
delete(cache.assumedPods, key)
delete(cache.orphanedPods, key)
+ delete(cache.podsCycleState, key)
}
return result
@@ -420,6 +431,7 @@ func (cache *SchedulerCache) removePod(pod *v1.Pod) {
delete(cache.assignedPods, key)
delete(cache.assumedPods, key)
delete(cache.orphanedPods, key)
+ delete(cache.podsCycleState, key)
cache.nodesInfoPodsWithAffinity = nil
cache.nodesInfoPodsWithReqAntiAffinity = nil
}
@@ -607,6 +619,28 @@ func (cache *SchedulerCache) updatePVCRefCounts(node
*framework.NodeInfo, remove
}
}
+func (cache *SchedulerCache) GetCycleState(pod *v1.Pod) *framework.CycleState {
+ return cache.podsCycleState[string(pod.UID)]
+}
+
+func (cache *SchedulerCache) StoreCycleState(pod *v1.Pod, cycleState
*framework.CycleState) {
+ cache.lock.Lock()
+ defer cache.lock.Unlock()
+ if pod == nil {
+ return
+ }
+ cache.podsCycleState[string(pod.UID)] = cycleState
+}
+
+func (cache *SchedulerCache) DeleteCycleState(pod *v1.Pod) {
+ cache.lock.Lock()
+ defer cache.lock.Unlock()
+ if pod == nil {
+ return
+ }
+ delete(cache.podsCycleState, string(pod.UID))
+}
+
func (cache *SchedulerCache) GetSchedulerCacheDao() SchedulerCacheDao {
cache.lock.RLock()
defer cache.lock.RUnlock()
diff --git a/pkg/cache/scheduler_callback.go b/pkg/cache/scheduler_callback.go
index dd53828f..381d223c 100644
--- a/pkg/cache/scheduler_callback.go
+++ b/pkg/cache/scheduler_callback.go
@@ -97,6 +97,8 @@ func (callback *AsyncRMCallback) UpdateAllocation(response
*si.AllocationRespons
if wait.Interrupted(err) && lastErr != nil {
err = lastErr
}
+ // delete the cycle state
+
callback.context.schedulerCache.DeleteCycleState(task.GetTaskPod())
if task.IsPlaceholder() {
// Placeholder tasks do not have volume
bindings, so AssumePod failure
// is unexpected and unrecoverable; wrap the
error with context.
@@ -111,6 +113,9 @@ func (callback *AsyncRMCallback) UpdateAllocation(response
*si.AllocationRespons
if utils.IsAssignedPod(task.GetTaskPod()) {
// task is already bound, fixup state and continue
task.MarkPreviouslyAllocated(alloc.AllocationKey,
alloc.NodeID)
+
+ // delete the cycle state
+
callback.context.schedulerCache.DeleteCycleState(task.GetTaskPod())
} else {
ev := NewAllocateTaskEvent(alloc.ApplicationID,
task.taskID, alloc.AllocationKey, alloc.NodeID)
dispatcher.Dispatch(ev)
@@ -220,6 +225,10 @@ func (callback *AsyncRMCallback) UpdateNode(response
*si.NodeResponse) error {
return nil
}
+func (callback *AsyncRMCallback) PreFilterPredicates(args
*si.PreFilterPredicatesArgs) *si.PreFilterPredicatesResponse {
+ return callback.context.PreFilter(args.AllocationKey, args.Allocate)
+}
+
func (callback *AsyncRMCallback) Predicates(args *si.PredicatesArgs) error {
return callback.context.IsPodFitNode(args.AllocationKey, args.NodeID,
args.Allocate)
}
diff --git a/pkg/cache/scheduler_callback_test.go
b/pkg/cache/scheduler_callback_test.go
index bf97c437..c71ce820 100644
--- a/pkg/cache/scheduler_callback_test.go
+++ b/pkg/cache/scheduler_callback_test.go
@@ -537,6 +537,33 @@ func testUpdateNode(t *testing.T, expectedEvent string,
response *si.NodeRespons
assert.NilError(t, err)
}
+func TestPredicatesPreFilter(t *testing.T) {
+ callback, context := initCallbackTest(t, false, false)
+ defer dispatcher.UnregisterAllEventHandlers()
+ defer dispatcher.Stop()
+ context.predManager = &mockPredicateManager{}
+
+ // pod not found
+ predicatesResults :=
callback.PreFilterPredicates(&si.PreFilterPredicatesArgs{AllocationKey:
"unknown", Allocate: true})
+ assert.Equal(t, predicatesResults.Success, false)
+ assert.Equal(t, len(predicatesResults.FeasibleNodes), 0)
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: "unknown",
+ UID: "unknown",
+ },
+ }) == nil)
+
+ // pod found
+ predicatesResults =
callback.PreFilterPredicates(&si.PreFilterPredicatesArgs{AllocationKey:
taskUID1, Allocate: true})
+ assert.Equal(t, predicatesResults.Success, true)
+ assert.Equal(t, len(predicatesResults.FeasibleNodes), 0)
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: taskUID1,
+ UID: taskUID1,
+ },
+ }) != nil)
+}
+
func TestPredicates(t *testing.T) {
callback, context := initCallbackTest(t, false, false)
defer dispatcher.UnregisterAllEventHandlers()
@@ -546,10 +573,36 @@ func TestPredicates(t *testing.T) {
// pod not found
err := callback.Predicates(&si.PredicatesArgs{AllocationKey: "unknown",
NodeID: fakeNodeName, Allocate: true})
assert.Error(t, err, "predicates were not run because pod was not found
in cache")
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: "unknown",
+ UID: "unknown",
+ },
+ }) == nil)
- // pod found
+ // pod found, node not found
+ err = callback.Predicates(&si.PredicatesArgs{AllocationKey: taskUID1,
NodeID: "unknown", Allocate: true})
+ assert.Error(t, err, "predicates were not run because node was not
found in cache")
+
+ // both pod and node found, cycle state not found
+ err = callback.Predicates(&si.PredicatesArgs{AllocationKey: taskUID1,
NodeID: fakeNodeName, Allocate: true})
+ assert.Error(t, err, "predicates were not run because pod's cycle state
was not found in cache")
+
+ // pod, node & cycle state found
+ results :=
callback.PreFilterPredicates(&si.PreFilterPredicatesArgs{AllocationKey:
taskUID1, Allocate: true})
+ assert.Equal(t, results.Success, true)
+ assert.Equal(t, len(results.FeasibleNodes), 0)
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: taskUID1,
+ UID: taskUID1,
+ },
+ }) != nil)
err = callback.Predicates(&si.PredicatesArgs{AllocationKey: taskUID1,
NodeID: fakeNodeName, Allocate: true})
assert.NilError(t, err)
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: taskUID1,
+ UID: taskUID1,
+ },
+ }) != nil)
}
func TestPreemptionPredicates(t *testing.T) {
@@ -561,11 +614,37 @@ func TestPreemptionPredicates(t *testing.T) {
// pod not found
resp :=
callback.PreemptionPredicates(&si.PreemptionPredicatesArgs{AllocationKey:
"unknown", NodeID: fakeNodeName, StartIndex: 0})
assert.Assert(t, !resp.Success, "response should have failed")
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: "unknown",
+ UID: "unknown",
+ },
+ }) == nil)
+
+ // pod found, node not found
+ resp =
callback.PreemptionPredicates(&si.PreemptionPredicatesArgs{AllocationKey:
taskUID1, NodeID: "unknown", StartIndex: 0})
+ assert.Assert(t, !resp.Success, "response should have failed")
- // pod found
+ // both pod and node found, cycle state not found
+ resp =
callback.PreemptionPredicates(&si.PreemptionPredicatesArgs{AllocationKey:
taskUID1, NodeID: fakeNodeName, StartIndex: 0})
+ assert.Assert(t, !resp.Success, "response should have failed")
+
+ // pod, node & cycle state found
+ results :=
callback.PreFilterPredicates(&si.PreFilterPredicatesArgs{AllocationKey:
taskUID1, Allocate: true})
+ assert.Equal(t, results.Success, true)
+ assert.Equal(t, len(results.FeasibleNodes), 0)
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: taskUID1,
+ UID: taskUID1,
+ },
+ }) != nil)
resp =
callback.PreemptionPredicates(&si.PreemptionPredicatesArgs{AllocationKey:
taskUID1, NodeID: fakeNodeName, StartIndex: 0, PreemptAllocationKeys:
[]string{taskUID1}})
assert.Assert(t, resp.Success, "response should have succeeded")
assert.Equal(t, int32(0), resp.Index)
+ assert.Assert(t,
callback.context.schedulerCache.GetCycleState(&v1.Pod{ObjectMeta:
apis.ObjectMeta{
+ Name: taskUID1,
+ UID: taskUID1,
+ },
+ }) != nil)
}
func TestSendEvent(t *testing.T) {
@@ -631,18 +710,22 @@ var _ predicates.PredicateManager =
&mockPredicateManager{}
type mockPredicateManager struct{}
-func (m *mockPredicateManager) EventsToRegister(_ fwk.QueueingHintFn)
[]fwk.ClusterEventWithHint {
- return nil
+func (m *mockPredicateManager) PreFilter(_ *v1.Pod, _ bool)
(map[string]*si.Empty, *framework.CycleState, error) {
+ return map[string]*si.Empty{}, framework.NewCycleState(), nil
}
-func (m *mockPredicateManager) Predicates(_ *v1.Pod, _ *framework.NodeInfo, _
bool) (string, error) {
+func (m *mockPredicateManager) Filter(_ *v1.Pod, _ *framework.NodeInfo, _
*framework.CycleState, _ bool) (string, error) {
return "", nil
}
-func (m *mockPredicateManager) PreemptionPredicates(_ *v1.Pod, _
*framework.NodeInfo, _ []*v1.Pod, _ int) int {
+func (m *mockPredicateManager) PreemptionFilter(_ *v1.Pod, _
*framework.NodeInfo, _ *framework.CycleState, _ []*v1.Pod, _ int) int {
return 0
}
+func (m *mockPredicateManager) EventsToRegister(_ fwk.QueueingHintFn)
[]fwk.ClusterEventWithHint {
+ return nil
+}
+
func initCallbackTest(t *testing.T, podAssigned, placeholder bool)
(*AsyncRMCallback, *Context) {
context, apiProvider := initContextAndAPIProviderForTest()
dispatcher.Start()
diff --git a/pkg/cache/task.go b/pkg/cache/task.go
index 2519b69e..20da78b6 100644
--- a/pkg/cache/task.go
+++ b/pkg/cache/task.go
@@ -392,8 +392,15 @@ func (task *Task) postTaskAllocated() {
task.lock.Unlock()
task.rescheduleOnBindFailure(allocationKey, nodeName,
"PodVolumesBindFailure",
fmt.Sprintf("Failed to bind volumes for %s on
node %s, it will be retried", alias, nodeName))
+
+ // delete the cycle state
+ task.context.schedulerCache.DeleteCycleState(pod)
return
}
+
+ // delete the cycle state
+ task.context.schedulerCache.DeleteCycleState(pod)
+
log.Log(log.ShimCacheTask).Debug("bind pod",
zap.String("podName", pod.Name),
zap.String("podUID", string(pod.UID)))
diff --git a/pkg/plugin/predicates/predicate_manager.go
b/pkg/plugin/predicates/predicate_manager.go
index 9b30b99a..085be78b 100644
--- a/pkg/plugin/predicates/predicate_manager.go
+++ b/pkg/plugin/predicates/predicate_manager.go
@@ -27,6 +27,7 @@ import (
"go.uber.org/zap"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/component-base/config/v1alpha1"
"k8s.io/klog/v2"
@@ -36,22 +37,23 @@ import (
apiConfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config/scheme"
"k8s.io/kubernetes/pkg/scheduler/framework"
- "k8s.io/kubernetes/pkg/scheduler/framework/plugins"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/names"
fwruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
"k8s.io/kubernetes/pkg/scheduler/metrics"
"github.com/apache/yunikorn-k8shim/pkg/log"
+ "github.com/apache/yunikorn-scheduler-interface/lib/go/si"
)
type PredicateManager interface {
EventsToRegister(queueingHintFn fwk.QueueingHintFn)
[]fwk.ClusterEventWithHint
- // Predicates checks if a pod can fit on a node.
+ PreFilter(pod *v1.Pod, allocate bool) (feasibleNodes
map[string]*si.Empty, cycleState *framework.CycleState, error error)
+ // Filter Predicates checks if a pod can fit on a node.
// Returns the name of the predicate plugin that failed (may be empty)
and any error encountered.
- Predicates(pod *v1.Pod, node *framework.NodeInfo, allocate bool)
(string, error)
+ Filter(pod *v1.Pod, node *framework.NodeInfo, cycleState
*framework.CycleState, allocate bool) (plugin string, error error)
// PreemptionPredicates checks if a pod can be scheduled on the node by
preempting victims.
// Returns the victim index that allows the pod to fit, or -1 if none.
- PreemptionPredicates(pod *v1.Pod, node *framework.NodeInfo, victims
[]*v1.Pod, startIndex int) int
+ PreemptionFilter(pod *v1.Pod, node *framework.NodeInfo, cycleState
*framework.CycleState, victims []*v1.Pod, startIndex int) (index int)
}
var _ PredicateManager = &predicateManagerImpl{}
@@ -131,28 +133,8 @@ func buildClusterEvents(actionMap
map[fwk.EventResource]fwk.ActionType, queueing
return events
}
-func (p *predicateManagerImpl) Predicates(pod *v1.Pod, node
*framework.NodeInfo, allocate bool) (string, error) {
- if allocate {
- return p.predicatesAllocate(pod, node)
- }
- return p.predicatesReserve(pod, node)
-}
-
-func (p *predicateManagerImpl) PreemptionPredicates(pod *v1.Pod, node
*framework.NodeInfo, victims []*v1.Pod, startIndex int) int {
+func (p *predicateManagerImpl) PreemptionFilter(pod *v1.Pod, node
*framework.NodeInfo, cycleState *framework.CycleState, victims []*v1.Pod,
startIndex int) int {
ctx := context.Background()
- state := framework.NewCycleState()
-
- // run prefilter checks as pod cannot be scheduled otherwise
- s, plugin, skip := p.runPreFilterPlugins(ctx, state,
*p.allocationPreFilters, pod, node)
- if !s.IsSuccess() && !s.IsSkip() {
- // prefilter check failed, log and return
- log.Log(log.ShimPredicates).Debug("PreFilter check failed
during preemption check",
- zap.String("podUID", string(pod.UID)),
- zap.String("plugin", plugin),
- zap.String("message", s.Message()))
-
- return -1
- }
// clone node so that we can modify it here for predicate checks
preemptingNode := node.Snapshot()
@@ -165,7 +147,7 @@ func (p *predicateManagerImpl) PreemptionPredicates(pod
*v1.Pod, node *framework
// loop through remaining pods
for i := startIndex; i < len(victims); i++ {
p.removePodFromNodeNoFail(preemptingNode, victims[i])
- status, _ := p.runFilterPlugins(ctx, *p.allocationFilters,
state, pod, preemptingNode, skip)
+ status, _ := p.runFilterPlugins(ctx, *p.allocationFilters,
cycleState, pod, preemptingNode)
if status.IsSuccess() {
return i
}
@@ -191,80 +173,90 @@ func (p *predicateManagerImpl)
removePodFromNodeNoFail(node fwk.NodeInfo, pod *v
}
}
-func (p *predicateManagerImpl) predicatesReserve(pod *v1.Pod, node
*framework.NodeInfo) (string, error) {
- ctx := context.Background()
- state := framework.NewCycleState()
- return p.podFitsNode(ctx, state, *p.reservationPreFilters,
*p.reservationFilters, pod, node)
-}
-
-func (p *predicateManagerImpl) predicatesAllocate(pod *v1.Pod, node
*framework.NodeInfo) (string, error) {
+func (p *predicateManagerImpl) PreFilter(pod *v1.Pod, allocate bool)
(map[string]*si.Empty, *framework.CycleState, error) {
ctx := context.Background()
- state := framework.NewCycleState()
- return p.podFitsNode(ctx, state, *p.allocationPreFilters,
*p.allocationFilters, pod, node)
-}
+ cycleState := framework.NewCycleState()
-func (p *predicateManagerImpl) podFitsNode(ctx context.Context, state
*framework.CycleState, preFilters []fwk.PreFilterPlugin, filters
[]fwk.FilterPlugin, pod *v1.Pod, node *framework.NodeInfo) (string, error) {
- // Run "prefilter" plugins.
- status, plugin, skip := p.runPreFilterPlugins(ctx, state, preFilters,
pod, node)
- if !status.IsSuccess() && !status.IsSkip() {
- return plugin, errors.New(status.Message())
+ var status *fwk.Status
+ var feasibleNodes map[string]*si.Empty
+ if allocate {
+ status, feasibleNodes = p.runPreFilterPlugins(ctx, cycleState,
*p.allocationPreFilters, pod)
+ } else {
+ status, feasibleNodes = p.runPreFilterPlugins(ctx, cycleState,
*p.reservationPreFilters, pod)
}
-
- // Run "filter" plugins on node
- status, plugin = p.runFilterPlugins(ctx, filters, state, pod, node,
skip)
- if !status.IsSuccess() {
- return plugin, errors.New(status.Message())
+ if !status.IsSuccess() && !status.IsSkip() {
+ return map[string]*si.Empty{}, nil, errors.New(status.Message())
}
- return "", nil
+ return feasibleNodes, cycleState, nil
}
-func (p *predicateManagerImpl) runPreFilterPlugins(ctx context.Context, state
*framework.CycleState, plugins []fwk.PreFilterPlugin, pod *v1.Pod, node
*framework.NodeInfo) (*fwk.Status, string, map[string]bool) {
- var mergedNodes *fwk.PreFilterResult
- skip := make(map[string]bool)
+func (p *predicateManagerImpl) runPreFilterPlugins(ctx context.Context,
cycleState *framework.CycleState, plugins []fwk.PreFilterPlugin, pod *v1.Pod)
(*fwk.Status, map[string]*si.Empty) {
+ skipPlugins := sets.New[string]()
+ feasibleNodes := make(map[string]*si.Empty)
allNodes, err := p.sharedLister.NodeInfos().List()
if err != nil {
log.Log(log.ShimPredicates).Error("failed to list nodes",
zap.Error(err))
- return fwk.AsStatus(err), "", skip
+ return fwk.AsStatus(err), feasibleNodes
}
+ mergedPreFilterResults := &fwk.PreFilterResult{}
for _, pl := range plugins {
plugin := pl.Name()
- nodes, status := p.runPreFilterPlugin(ctx, pl, state, pod,
allNodes)
+ nodes, status := pl.PreFilter(ctx, cycleState, pod, allNodes)
if status.IsSkip() {
- skip[plugin] = true
- } else if !status.IsSuccess() {
- if status.IsRejected() {
- return status, "", skip
- }
+ skipPlugins.Insert(plugin)
+ continue
+ }
+ if status.IsRejected() {
+ return status, map[string]*si.Empty{}
+ }
+ if !status.IsSuccess() {
err := errors.New(status.Message())
log.Log(log.ShimPredicates).Error("failed running
PreFilter plugin",
zap.String("pluginName", plugin),
zap.String("pod", fmt.Sprintf("%s/%s",
pod.Namespace, pod.Name)),
zap.Error(err))
- return fwk.AsStatus(errors.Join(fmt.Errorf("running
PreFilter plugin %q: ", plugin), err)), plugin, skip
+ return fwk.AsStatus(errors.Join(fmt.Errorf("running
PreFilter plugin %q: ", plugin), err)), map[string]*si.Empty{}
}
- // Merge is nil safe and returns a new PreFilterResult result
if mergedNodes was nil
- mergedNodes = mergedNodes.Merge(nodes)
- if !mergedNodes.AllNodes() &&
!mergedNodes.NodeNames.Has(node.Node().Name) {
- return fwk.NewStatus(fwk.UnschedulableAndUnresolvable,
"node not eligible"), plugin, skip
+ mergedPreFilterResults = mergedPreFilterResults.Merge(nodes)
+ }
+ if mergedPreFilterResults != nil {
+ for n := range mergedPreFilterResults.NodeNames {
+ feasibleNodes[n] = &si.Empty{}
}
}
-
- return nil, "", skip
+ if skipPlugins.Len() > 0 {
+ cycleState.SetSkipFilterPlugins(skipPlugins)
+ }
+ return nil, feasibleNodes
}
-func (p *predicateManagerImpl) runPreFilterPlugin(ctx context.Context, pl
fwk.PreFilterPlugin, state *framework.CycleState, pod *v1.Pod, allNodes
[]fwk.NodeInfo) (*fwk.PreFilterResult, *fwk.Status) {
- return pl.PreFilter(ctx, state, pod, allNodes)
+func (p *predicateManagerImpl) Filter(pod *v1.Pod, node *framework.NodeInfo,
cycleState *framework.CycleState, allocate bool) (string, error) {
+ ctx := context.Background()
+
+ var status *fwk.Status
+ var plugin string
+ if allocate {
+ status, plugin = p.runFilterPlugins(ctx, *p.allocationFilters,
cycleState, pod, node)
+ } else {
+ status, plugin = p.runFilterPlugins(ctx, *p.reservationFilters,
cycleState, pod, node)
+ }
+ if !status.IsSuccess() {
+ return plugin, errors.New(status.Message())
+ }
+ return "", nil
}
-func (p *predicateManagerImpl) runFilterPlugins(ctx context.Context, plugins
[]fwk.FilterPlugin, state *framework.CycleState, pod *v1.Pod, nodeInfo
fwk.NodeInfo, skip map[string]bool) (*fwk.Status, string) {
+func (p *predicateManagerImpl) runFilterPlugins(ctx context.Context, plugins
[]fwk.FilterPlugin, cycleState *framework.CycleState, pod *v1.Pod, nodeInfo
fwk.NodeInfo) (*fwk.Status, string) {
+ skipPlugins := cycleState.GetSkipFilterPlugins()
for _, pl := range plugins {
plugin := pl.Name()
+
// skip plugin if prefilter returned skip
- if skip[plugin] {
+ if skipPlugins.Has(plugin) {
continue
}
- status := p.runFilterPlugin(ctx, pl, state, pod, nodeInfo)
+ status := pl.Filter(ctx, cycleState, pod, nodeInfo)
if !status.IsSuccess() {
if !status.IsRejected() {
// Filter plugins are not supposed to return
any status other than
@@ -282,10 +274,6 @@ func (p *predicateManagerImpl) runFilterPlugins(ctx
context.Context, plugins []f
return fwk.NewStatus(fwk.Success), ""
}
-func (p *predicateManagerImpl) runFilterPlugin(ctx context.Context, pl
fwk.FilterPlugin, state *framework.CycleState, pod *v1.Pod, nodeInfo
fwk.NodeInfo) *fwk.Status {
- return pl.Filter(ctx, state, pod, nodeInfo)
-}
-
// EnableOptionalKubernetesFeatureGates ensures that any optional Kubernetes
feature gates that YuniKorn supports are
// enabled. Currently, as of Kubernetes 1.32, this includes PodLevelResources
and InPlacePodVerticalScaling. These are
// both safe to enable as part of our default configuration, as they also
require the appropriate feature gates to be
@@ -303,7 +291,7 @@ func EnableOptionalKubernetesFeatureGates() {
}
}
-func NewPredicateManager(handle fwk.Handle) PredicateManager {
+func NewPredicateManager(handle fwk.Handle, registry fwruntime.Registry,
config *apiConfig.KubeSchedulerConfiguration) PredicateManager {
/*
Default K8S plugins as of 1.32 that implement PreFilter:
NodeAffinity
@@ -372,11 +360,13 @@ func NewPredicateManager(handle fwk.Handle)
PredicateManager {
"*": true,
}
- return newPredicateManagerInternal(handle, reservationPreFilters,
allocationPreFilters, reservationFilters, allocationFilters)
+ return newPredicateManagerInternal(handle, registry, config,
reservationPreFilters, allocationPreFilters, reservationFilters,
allocationFilters)
}
func newPredicateManagerInternal(
handle fwk.Handle,
+ registry fwruntime.Registry,
+ config *apiConfig.KubeSchedulerConfiguration,
reservationPreFilters map[string]bool,
allocationPreFilters map[string]bool,
reservationFilters map[string]bool,
@@ -385,21 +375,13 @@ func newPredicateManagerInternal(
if metrics.Goroutines == nil {
metrics.InitMetrics()
}
-
- pluginRegistry := plugins.NewInTreeRegistry()
-
- cfg, err := defaultConfig() // latest.Default()
- if err != nil {
- log.Log(log.ShimPredicates).Fatal("Unable to get default
predicate config", zap.Error(err))
- }
-
- profile := cfg.Profiles[0] // first profile is default
+ profile := config.Profiles[0] // first profile is default
registeredPlugins := profile.Plugins
createdPlugins := make([]fwk.Plugin, 0)
// As of SchedulerConfiguration v1, all plugins implement MultiPoint,
therefore we need to instantiate each one and
// check to see what interfaces it implements dynamically
- createPlugins(handle, pluginRegistry, ®isteredPlugins.MultiPoint,
&createdPlugins)
+ createPlugins(handle, registry, ®isteredPlugins.MultiPoint,
&createdPlugins)
resPre := make([]fwk.Plugin, 0)
allocPre := make([]fwk.Plugin, 0)
@@ -445,7 +427,7 @@ func filterPlugins(plugins []fwk.Plugin)
*[]fwk.FilterPlugin {
return &result
}
-func defaultConfig() (*apiConfig.KubeSchedulerConfiguration, error) {
+func DefaultConfig() (*apiConfig.KubeSchedulerConfiguration, error) {
versionedCfg := schedConfig.KubeSchedulerConfiguration{}
versionedCfg.DebuggingConfiguration =
*v1alpha1.NewRecommendedDebuggingConfiguration()
diff --git a/pkg/plugin/predicates/predicate_manager_test.go
b/pkg/plugin/predicates/predicate_manager_test.go
index 0ab2b52c..eb592161 100644
--- a/pkg/plugin/predicates/predicate_manager_test.go
+++ b/pkg/plugin/predicates/predicate_manager_test.go
@@ -19,6 +19,8 @@
package predicates
import (
+ "context"
+ "errors"
"strconv"
"strings"
"testing"
@@ -28,12 +30,16 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime2 "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2"
fwk "k8s.io/kube-scheduler/framework"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/features"
+ apiConfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/framework"
+ "k8s.io/kubernetes/pkg/scheduler/framework/plugins"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
@@ -42,6 +48,7 @@ import (
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeunschedulable"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodevolumelimits"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/podtopologyspread"
+ "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
"k8s.io/kubernetes/pkg/util/taints"
"github.com/apache/yunikorn-k8shim/pkg/client"
@@ -55,23 +62,60 @@ var (
hugePageResourceA =
v1helper.HugePageResourceName(resource.MustParse("2Mi"))
)
-func TestPreemptionPredicatesEmpty(t *testing.T) {
- ep := enabledPlugins(noderesources.Name)
- handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+const (
+ mockPreFilterPlugin = "mock-prefilter-plugin"
+ mockFilterPlugin = "mock-filter-plugin"
+ injectReason = "injected status"
+ injectFilterReason = "injected filter status"
+)
- pod := &v1.Pod{}
- node := framework.NewNodeInfo()
- node.SetNode(&v1.Node{})
- victims := make([]*v1.Pod, 0)
- index := predicateManager.PreemptionPredicates(pod, node, victims, 0)
- assert.Equal(t, index, -1, "should not find any victim index after
preemption check")
+type injectedResult struct {
+ PreFilterResult *fwk.PreFilterResult `json:"preFilterResult,omitempty"`
+ PreFilterStatus int `json:"preFilterStatus,omitempty"`
+ FilterStatus int `json:"filterStatus,omitempty"`
+}
+
+// MockPreFilterPlugin implements PreFilter interface.
+type MockPreFilterPlugin struct {
+ name string
+ inj injectedResult
+}
+
+func (pl *MockPreFilterPlugin) PreFilterExtensions() fwk.PreFilterExtensions {
+ return nil
+}
+
+func (pl *MockPreFilterPlugin) Name() string {
+ return pl.name
}
-func TestPreemptionPredicates(t *testing.T) {
+func (pl *MockPreFilterPlugin) PreFilter(ctx context.Context, state
fwk.CycleState, p *v1.Pod, nodes []fwk.NodeInfo) (*fwk.PreFilterResult,
*fwk.Status) {
+ return pl.inj.PreFilterResult,
fwk.NewStatus(fwk.Code(pl.inj.PreFilterStatus), injectReason)
+}
+
+// MockFilterPlugin implements Filter interface.
+type MockFilterPlugin struct {
+ name string
+ inj injectedResult
+}
+
+func (pl *MockFilterPlugin) Filter(ctx context.Context, state fwk.CycleState,
pod *v1.Pod, nodeInfo fwk.NodeInfo) *fwk.Status {
+ return fwk.NewStatus(fwk.Code(pl.inj.FilterStatus), injectFilterReason)
+}
+
+func (pl *MockFilterPlugin) Name() string {
+ return pl.name
+}
+
+func TestPreemptionFilterWithVictims(t *testing.T) {
ep := enabledPlugins(noderesources.Name)
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
+
+ emptyNode := framework.NewNodeInfo()
+ emptyNode.SetNode(&v1.Node{})
pod := newResourcePod(framework.Resource{MilliCPU: 500, Memory:
5000000})
pod.Name = "smallpod"
@@ -103,23 +147,39 @@ func TestPreemptionPredicates(t *testing.T) {
node.AddPod(victims[2])
node.AddPod(victims[3])
- // all but 1 existing pod should need removing
- index := predicateManager.PreemptionPredicates(pod, node, victims, 1)
- assert.Equal(t, index, 2, "wrong victim index")
-
// try again, but with too many resources requested
- pod = newResourcePod(framework.Resource{MilliCPU: 1500, Memory:
15000000})
- pod.Name = "largepod"
- pod.UID = "largepod"
+ largePod := newResourcePod(framework.Resource{MilliCPU: 1500, Memory:
15000000})
+ largePod.Name = "largepod"
+ largePod.UID = "largepod"
+
+ tests := []struct {
+ name string
+ pod *v1.Pod
+ node *framework.NodeInfo
+ victims []*v1.Pod
+ expectedIndex int
+ }{
+ {"invalid pod and no victims", &v1.Pod{}, emptyNode,
make([]*v1.Pod, 0), -1},
+ {"valid pod with available victims", pod, node, victims, 2},
+ {"valid pod with not suitable victims", largePod, node,
victims, -1},
+ }
- index = predicateManager.PreemptionPredicates(pod, node, victims, 1)
- assert.Equal(t, index, -1, "should not find any victim index after
preemption check")
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ _, cycleState, err :=
predicateManager.PreFilter(tt.pod, true)
+ assert.NilError(t, err)
+ index := predicateManager.PreemptionFilter(tt.pod,
tt.node, cycleState, tt.victims, 1)
+ assert.Equal(t, index, tt.expectedIndex, "wrong victim
index")
+ })
+ }
}
func TestEventsToRegister(t *testing.T) {
ep := enabledPlugins(nodename.Name, interpodaffinity.Name,
podtopologyspread.Name)
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
var queueingHintFn fwk.QueueingHintFn = func(logger klog.Logger, pod
*v1.Pod, oldObj, newObj interface{}) (fwk.QueueingHint, error) {
// illegal sentinel to ensure we called the correct function
@@ -141,7 +201,9 @@ func TestEventsToRegister(t *testing.T) {
func TestPodFitsHost(t *testing.T) {
ep := enabledPlugins(nodename.Name)
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
tests := []struct {
pod *v1.Pod
node *v1.Node
@@ -188,7 +250,7 @@ func TestPodFitsHost(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
nodeInfo := framework.NewNodeInfo()
nodeInfo.SetNode(test.node)
- plugin, err := predicateManager.Predicates(test.pod,
nodeInfo, true)
+ plugin, err := predicateManager.Filter(test.pod,
nodeInfo, framework.NewCycleState(), true)
if (err == nil) != test.fits {
t.Errorf("%s expected fit state '%t' did not
match real state and err = %v, plugin = %v", test.name, test.fits, err, plugin)
}
@@ -224,7 +286,9 @@ func newPod(host string, hostPortInfos ...string) *v1.Pod {
func TestPodFitsHostPorts(t *testing.T) {
ep := enabledPlugins(nodeports.Name)
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
tests := []struct {
pod *v1.Pod
@@ -326,7 +390,10 @@ func TestPodFitsHostPorts(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- plugin, err := predicateManager.Predicates(test.pod,
test.nodeInfo, true)
+ _, cycleState, err :=
predicateManager.PreFilter(test.pod, true)
+ assert.NilError(t, err)
+ assert.Assert(t, cycleState != nil)
+ plugin, err := predicateManager.Filter(test.pod,
test.nodeInfo, cycleState, true)
if (err == nil) != test.fits {
t.Errorf("%s expected fit state '%t' did not
match real state and err = %v, plugin = %v", test.name, test.fits, err, plugin)
}
@@ -338,7 +405,9 @@ func TestPodFitsHostPorts(t *testing.T) {
func TestPodFitsSelector(t *testing.T) {
ep := enabledPlugins(nodeports.Name, nodeaffinity.Name)
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
tests := []struct {
pod *v1.Pod
@@ -1020,8 +1089,10 @@ func TestPodFitsSelector(t *testing.T) {
}}
nodeInfo := framework.NewNodeInfo()
nodeInfo.SetNode(&node)
-
- plugin, err := predicateManager.Predicates(test.pod,
nodeInfo, true)
+ _, cycleState, err :=
predicateManager.PreFilter(test.pod, true)
+ assert.NilError(t, err)
+ assert.Assert(t, cycleState != nil)
+ plugin, err := predicateManager.Filter(test.pod,
nodeInfo, cycleState, true)
if (err == nil) != test.fits {
t.Errorf("%s expected fit state '%t' did not
match real state and err = %v, plugin = %v", test.name, test.fits, err, plugin)
}
@@ -1095,7 +1166,9 @@ func TestEnableOptionalKubernetesFeatureGates(t
*testing.T) {
func TestRunGeneralPredicates(t *testing.T) {
ep := enabledPlugins(noderesources.Name, nodename.Name, nodeports.Name,
nodevolumelimits.CSIName)
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
resourceTests := []struct {
pod *v1.Pod
@@ -1159,7 +1232,10 @@ func TestRunGeneralPredicates(t *testing.T) {
for _, test := range resourceTests {
t.Run(test.name, func(t *testing.T) {
test.nodeInfo.SetNode(test.node)
- plugin, err := predicateManager.Predicates(test.pod,
test.nodeInfo, true)
+ _, cycleState, err :=
predicateManager.PreFilter(test.pod, true)
+ assert.NilError(t, err)
+ assert.Assert(t, cycleState != nil)
+ plugin, err := predicateManager.Filter(test.pod,
test.nodeInfo, cycleState, true)
if (err == nil) != test.fits {
t.Errorf("%s expected fit state '%t' did not
match real state and err = %v, plugin = %v", test.name, test.fits, err, plugin)
}
@@ -1171,7 +1247,9 @@ func TestRunGeneralPredicates(t *testing.T) {
func TestInterPodAffinity(t *testing.T) {
ep := enabledPlugins(interpodaffinity.Name, nodeaffinity.Name)
handle, lister := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
podLabel := map[string]string{"service": "securityscan"}
labels1 := map[string]string{
@@ -2104,7 +2182,11 @@ func TestInterPodAffinity(t *testing.T) {
nodeInfo := framework.NewNodeInfo(podsOnNode...)
nodeInfo.SetNode(test.node)
lister.NodeLister().Set([]fwk.NodeInfo{nodeInfo})
- pl, err := predicateManager.Predicates(test.pod,
nodeInfo, true)
+
+ _, cycleState, err :=
predicateManager.PreFilter(test.pod, true)
+ assert.NilError(t, err)
+ assert.Assert(t, cycleState != nil)
+ pl, err := predicateManager.Filter(test.pod, nodeInfo,
cycleState, true)
if (err == nil) != test.fits {
t.Errorf("%s expected fit state '%t' did not
match real state and err = %v, plugin = %v", test.name, test.fits, err, pl)
}
@@ -2129,14 +2211,16 @@ func TestReserveAlloc(t *testing.T) {
// no predicates configured that are run by reservations
ep := enabledPlugins()
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
- _, err := predicateManager.Predicates(pod, nodeInfo, false)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
+ _, err = predicateManager.Filter(pod, nodeInfo,
framework.NewCycleState(), false)
assert.NilError(t, err, "error should have been nil, no predicates
given")
// add one predicate also run by reservations
ep[nodeunschedulable.Name] = true
- predicateManager = newPredicateManagerInternal(handle, ep, ep, ep, ep)
- _, err = predicateManager.Predicates(pod, nodeInfo, false)
+ predicateManager = newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
+ _, err = predicateManager.Filter(pod, nodeInfo,
framework.NewCycleState(), false)
assert.NilError(t, err, "error should have been nil, node is
schedulable")
// make the node unschedulable
@@ -2147,7 +2231,7 @@ func TestReserveAlloc(t *testing.T) {
node.Spec.Unschedulable = true
assert.NilError(t, err, "failed to add taint")
nodeInfo.SetNode(node)
- _, err = predicateManager.Predicates(pod, nodeInfo, false)
+ _, err = predicateManager.Filter(pod, nodeInfo,
framework.NewCycleState(), false)
if err == nil {
t.Errorf("error should not have been nil, predicate should have
failed")
}
@@ -2170,7 +2254,9 @@ func TestReserveNodeSelector(t *testing.T) {
ep := enabledPlugins(nodename.Name, nodeports.Name,
podtopologyspread.Name, nodeaffinity.Name)
handle, _ := getFrameworkHandle()
- predicateManager := newPredicateManagerInternal(handle, ep, ep, ep, ep)
+ config, err := DefaultConfig()
+ assert.NilError(t, err)
+ predicateManager := newPredicateManagerInternal(handle,
plugins.NewInTreeRegistry(), config, ep, ep, ep, ep)
testCases := []struct {
name string
@@ -2187,7 +2273,10 @@ func TestReserveNodeSelector(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
pod.Spec.NodeSelector = tc.nodeSelectors
node.Labels = tc.nodeLabels
- plugin, err := predicateManager.Predicates(pod,
nodeInfo, false)
+ _, cycleState, err := predicateManager.PreFilter(pod,
true)
+ assert.NilError(t, err)
+ assert.Assert(t, cycleState != nil)
+ plugin, err := predicateManager.Filter(pod, nodeInfo,
cycleState, false)
log.Log(log.Test).Info("reservation predicates called",
zap.Error(err), zap.String("plugin", plugin))
if tc.errorExpected {
assert.Assert(t, err != nil, "An error is
expected")
@@ -2198,6 +2287,336 @@ func TestReserveNodeSelector(t *testing.T) {
}
}
+func TestPreFilter(t *testing.T) {
+ node1 := "node1"
+ node2 := "node2"
+ node3 := "node3"
+ nodes, nodes1, nodes2, nodes3 := make(sets.Set[string]),
make(sets.Set[string]), make(sets.Set[string]), make(sets.Set[string])
+ nodes.Insert(node1)
+ nodes1.Insert(node1)
+ nodes1.Insert(node2)
+ nodes2.Insert(node1)
+ nodes2.Insert(node3)
+ nodes3.Insert(node2)
+
+ pod := &v1.Pod{
+ Spec: v1.PodSpec{
+ NodeName: "foo",
+ },
+ }
+ testCases := []struct {
+ name string
+ pod *v1.Pod
+ plugins []*MockPreFilterPlugin
+ feasibleNodes []string
+ skippedPlugins []string
+ errorExpected error
+ }{
+ {"success", pod,
+ []*MockPreFilterPlugin{
+ {name: mockPreFilterPlugin, inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{NodeNames: nodes},
PreFilterStatus: int(fwk.Success)}},
+ {name: mockPreFilterPlugin + "-skip", inj:
injectedResult{PreFilterStatus: int(fwk.Skip)}},
+ },
+ []string{node1},
+ []string{"mock-prefilter-plugin-skip"}, nil,
+ },
+ {"intersection of nodes", pod,
+ []*MockPreFilterPlugin{
+ {name: mockPreFilterPlugin + "-1", inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{NodeNames: nodes1},
PreFilterStatus: int(fwk.Success)}},
+ {name: mockPreFilterPlugin + "-2", inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{NodeNames: nodes2},
PreFilterStatus: int(fwk.Success)}},
+ },
+ []string{node1},
+ nil, nil,
+ },
+
+ {"disjoint set of nodes", pod,
+ []*MockPreFilterPlugin{
+ {name: mockPreFilterPlugin + "-1", inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{NodeNames: nodes},
PreFilterStatus: int(fwk.Success)}},
+ {name: mockPreFilterPlugin + "-2", inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{NodeNames: nodes3},
PreFilterStatus: int(fwk.Success)}},
+ },
+ []string{},
+ nil, nil,
+ },
+
+ {"rejected - unschedulable and unresolvable", pod,
+ []*MockPreFilterPlugin{
+ {name: mockPreFilterPlugin, inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{}, PreFilterStatus:
int(fwk.UnschedulableAndUnresolvable)}},
+ },
+ []string{},
+ nil, errors.New("UnschedulableAndUnresolvable"),
+ },
+ {"rejected - unschedulable", pod,
+ []*MockPreFilterPlugin{
+ {name: mockPreFilterPlugin, inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{}, PreFilterStatus:
int(fwk.Unschedulable)}},
+ },
+ []string{},
+ nil, errors.New("unschedulable"),
+ },
+ {"error", pod,
+ []*MockPreFilterPlugin{
+ {name: mockPreFilterPlugin, inj:
injectedResult{PreFilterResult: &fwk.PreFilterResult{}, PreFilterStatus:
int(fwk.Error)}},
+ },
+ []string{},
+ nil, errors.New("error"),
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ r := make(runtime.Registry)
+ enabled := make([]apiConfig.Plugin, len(tc.plugins))
+ var eps []string
+ for i, p := range tc.plugins {
+ enabled[i].Name = p.name
+ eps = append(eps, p.name)
+ if err := r.Register(p.name, func(_
context.Context, _ runtime2.Object, fh fwk.Handle) (fwk.Plugin, error) {
+ return p, nil
+ }); err != nil {
+ t.Fatalf("fail to register PreFilter
plugin (%s)", p.Name())
+ }
+ }
+ for _, allocate := range []bool{true, false} {
+ ep := enabledPlugins(eps...)
+ handle, _ := getFrameworkHandle()
+ config, err := prepareConfig(eps)
+ assert.NilError(t, err)
+ var resPreFilters map[string]bool
+ var allocPreFilters map[string]bool
+ if allocate {
+ allocPreFilters = ep
+ } else {
+ resPreFilters = ep
+ }
+ p := newPredicateManagerInternal(handle, r,
config, resPreFilters, allocPreFilters, nil, nil)
+ feasibleNodes, cycleState, filterErr :=
p.PreFilter(tc.pod, allocate)
+ if tc.skippedPlugins != nil {
+ for _, sp := range tc.skippedPlugins {
+ assert.Assert(t,
cycleState.GetSkipFilterPlugins().Has(sp) == true, nil)
+ }
+ }
+ if tc.feasibleNodes != nil {
+ assert.Equal(t, len(tc.feasibleNodes),
len(feasibleNodes))
+ for _, fn := range tc.feasibleNodes {
+ assert.Assert(t,
feasibleNodes[fn] != nil, nil)
+ }
+ }
+ if tc.errorExpected != nil {
+ assert.Assert(t, filterErr != nil,
"error should not be nil")
+ }
+ }
+ })
+ }
+}
+
+func TestFilter(t *testing.T) {
+ pod := &v1.Pod{
+ Spec: v1.PodSpec{
+ NodeName: "foo",
+ },
+ }
+ node := &v1.Node{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ }}
+ skip := make(sets.Set[string])
+ testCases := []struct {
+ name string
+ pod *v1.Pod
+ filterPlugins []*MockFilterPlugin
+ skippedPlugins sets.Set[string]
+ errorExpected error
+ }{
+ {"success", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.Success)}},
+ },
+ nil, nil,
+ },
+ {
+ "skip", pod,
+ []*MockFilterPlugin{
+ // Inject error code so that if not skipped
error would be thrown
+ {name: mockFilterPlugin + "-skip", inj:
injectedResult{FilterStatus: int(fwk.Error)}},
+ },
+ skip.Insert("mock-filter-plugin-skip"), nil,
+ },
+ {"rejected - unschedulable and unresolvable", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.UnschedulableAndUnresolvable)}},
+ },
+ nil, errors.New("UnschedulableAndUnresolvable"),
+ },
+ {"rejected - unschedulable", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.Unschedulable)}},
+ },
+ nil, errors.New("unschedulable"),
+ },
+ {"error", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.Error)}},
+ },
+ nil, errors.New("error"),
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ r := make(runtime.Registry)
+ enabled := make([]apiConfig.Plugin,
len(tc.filterPlugins))
+ var eps []string
+ for i, p := range tc.filterPlugins {
+ enabled[i].Name = p.name
+ eps = append(eps, p.name)
+ if err := r.Register(p.name, func(_
context.Context, _ runtime2.Object, fh fwk.Handle) (fwk.Plugin, error) {
+ return p, nil
+ }); err != nil {
+ t.Fatalf("fail to register PreFilter
plugin (%s)", p.Name())
+ }
+ }
+ for _, allocate := range []bool{true, false} {
+ ep := enabledPlugins(eps...)
+ handle, _ := getFrameworkHandle()
+ config, err := prepareConfig(eps)
+ assert.NilError(t, err)
+ var resFilters map[string]bool
+ var allocFilters map[string]bool
+ if allocate {
+ allocFilters = ep
+ } else {
+ resFilters = ep
+ }
+ p := newPredicateManagerInternal(handle, r,
config, nil, nil, resFilters, allocFilters)
+ cycleState := framework.NewCycleState()
+ if tc.skippedPlugins != nil {
+
cycleState.SetSkipFilterPlugins(tc.skippedPlugins)
+ }
+ nodeInfo := framework.NewNodeInfo()
+ nodeInfo.SetNode(node)
+ filter, err := p.Filter(tc.pod, nodeInfo,
cycleState, allocate)
+ if tc.errorExpected != nil {
+ assert.Assert(t, err != nil, "error
should not be nil")
+ assert.Equal(t, filter,
tc.filterPlugins[0].Name())
+ } else {
+ assert.Equal(t, filter, "")
+ }
+ }
+ })
+ }
+}
+
+func TestPreemptionFilter(t *testing.T) {
+ pod := &v1.Pod{
+ Spec: v1.PodSpec{
+ NodeName: "foo",
+ },
+ }
+ node := &v1.Node{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ }}
+ skip := make(sets.Set[string])
+ testCases := []struct {
+ name string
+ pod *v1.Pod
+ filterPlugins []*MockFilterPlugin
+ skippedPlugins sets.Set[string]
+ errorExpected error
+ }{
+ {"success", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.Success)}},
+ },
+ nil, nil,
+ },
+ {
+ "skip", pod,
+ []*MockFilterPlugin{
+ // Inject error code so that if not skipped
error would be thrown
+ {name: mockFilterPlugin + "-skip", inj:
injectedResult{FilterStatus: int(fwk.Error)}},
+ },
+ skip.Insert("mock-filter-plugin-skip"), nil,
+ },
+ {"rejected - unschedulable and unresolvable", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.UnschedulableAndUnresolvable)}},
+ },
+ nil, errors.New("UnschedulableAndUnresolvable"),
+ },
+ {"rejected - unschedulable", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.Unschedulable)}},
+ },
+ nil, errors.New("unschedulable"),
+ },
+ {"error", pod,
+ []*MockFilterPlugin{
+ {name: mockFilterPlugin, inj:
injectedResult{FilterStatus: int(fwk.Error)}},
+ },
+ nil, errors.New("error"),
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ r := make(runtime.Registry)
+ enabled := make([]apiConfig.Plugin,
len(tc.filterPlugins))
+ var eps []string
+ for i, p := range tc.filterPlugins {
+ enabled[i].Name = p.name
+ eps = append(eps, p.name)
+ if err := r.Register(p.name, func(_
context.Context, _ runtime2.Object, fh fwk.Handle) (fwk.Plugin, error) {
+ return p, nil
+ }); err != nil {
+ t.Fatalf("fail to register PreFilter
plugin (%s)", p.Name())
+ }
+ }
+ ep := enabledPlugins(eps...)
+ handle, _ := getFrameworkHandle()
+ config, err := prepareConfig(eps)
+ assert.NilError(t, err)
+ p := newPredicateManagerInternal(handle, r, config,
nil, nil, nil, ep)
+ cycleState := framework.NewCycleState()
+ if tc.skippedPlugins != nil {
+
cycleState.SetSkipFilterPlugins(tc.skippedPlugins)
+ }
+ nodeInfo := framework.NewNodeInfo()
+ nodeInfo.SetNode(node)
+
+ victims := []*v1.Pod{
+ newResourcePod(framework.Resource{MilliCPU:
100, Memory: 1000000}),
+ }
+ victims[0].Name = "pod0"
+ idx := p.PreemptionFilter(tc.pod, nodeInfo, cycleState,
victims, 0)
+ if tc.errorExpected != nil {
+ assert.Equal(t, idx, -1)
+ } else {
+ assert.Equal(t, idx, 0)
+ }
+ })
+ }
+}
+
+func prepareConfig(plugins []string) (*apiConfig.KubeSchedulerConfiguration,
error) {
+ var ep []apiConfig.Plugin
+ cfg := apiConfig.KubeSchedulerConfiguration{}
+ if len(plugins) > 0 {
+ for _, pl := range plugins {
+ ep = append(ep, apiConfig.Plugin{Name: pl})
+ }
+ cfg.Profiles = []apiConfig.KubeSchedulerProfile{
+ {
+ SchedulerName: "default-scheduler",
+ Plugins: &apiConfig.Plugins{
+ MultiPoint: apiConfig.PluginSet{
+ Enabled: ep,
+ },
+ },
+ },
+ }
+ }
+ return &cfg, nil
+}
+
func enabledPlugins(name ...string) map[string]bool {
pm := make(map[string]bool)
for _, k := range name {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]