@@ -8,14 +8,18 @@ import (
88 "os"
99
1010 kaiv1 "github.com/kai-scheduler/KAI-scheduler/pkg/apis/kai/v1"
11+ kaiv1admission "github.com/kai-scheduler/KAI-scheduler/pkg/apis/kai/v1/admission"
1112 kaiv1binder "github.com/kai-scheduler/KAI-scheduler/pkg/apis/kai/v1/binder"
13+ kaiv1scheduler "github.com/kai-scheduler/KAI-scheduler/pkg/apis/kai/v1/scheduler"
1214 "github.com/kai-scheduler/KAI-scheduler/pkg/common/constants"
1315 . "github.com/onsi/ginkgo/v2"
1416 . "github.com/onsi/gomega"
1517
1618 nvidiav1 "github.com/kai-scheduler/KAI-scheduler/third_party/nvidia/gpu-operator/api/nvidia/v1"
1719
1820 appsv1 "k8s.io/api/apps/v1"
21+ policyv1 "k8s.io/api/policy/v1"
22+ apierrors "k8s.io/apimachinery/pkg/api/errors"
1923 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2024 "k8s.io/apimachinery/pkg/types"
2125 "k8s.io/utils/ptr"
@@ -116,4 +120,169 @@ var _ = Describe("KAIConfigController", Ordered, func() {
116120 })
117121 })
118122
123+ Context ("Reconciling the admission PodDisruptionBudget" , Ordered , func () {
124+ const pdbName = "admission"
125+
126+ updateAdmissionPDBConfig := func (ctx context.Context , replicas int32 , enabled bool ) {
127+ Eventually (func () error {
128+ currentConfig := & kaiv1.Config {}
129+ if err := k8sClient .Get (
130+ ctx ,
131+ types.NamespacedName {Name : constants .DefaultKAIConfigSingeltonInstanceName },
132+ currentConfig ,
133+ ); err != nil {
134+ return err
135+ }
136+ currentConfig .Spec .Admission = & kaiv1admission.Admission {
137+ Replicas : ptr .To (replicas ),
138+ Service : & v1common.Service {
139+ Enabled : ptr .To (true ),
140+ PodDisruptionBudget : & v1common.PodDisruptionBudget {
141+ Enabled : ptr .To (enabled ),
142+ MaxUnavailable : ptr .To (int32 (1 )),
143+ },
144+ },
145+ }
146+ return k8sClient .Update (ctx , currentConfig )
147+ }, "10s" , "200ms" ).Should (Succeed ())
148+ }
149+
150+ getPDB := func (ctx context.Context ) (* policyv1.PodDisruptionBudget , error ) {
151+ pdb := & policyv1.PodDisruptionBudget {}
152+ err := k8sClient .Get (ctx , types.NamespacedName {
153+ Name : pdbName ,
154+ Namespace : kaiConfig .Spec .Namespace ,
155+ }, pdb )
156+ return pdb , err
157+ }
158+
159+ It ("creates and watches the PDB" , func (ctx context.Context ) {
160+ updateAdmissionPDBConfig (ctx , 2 , true )
161+
162+ Eventually (func (g Gomega ) {
163+ pdb , err := getPDB (ctx )
164+ g .Expect (err ).NotTo (HaveOccurred ())
165+ g .Expect (metav1 .GetControllerOf (pdb )).NotTo (BeNil ())
166+ g .Expect (metav1 .GetControllerOf (pdb ).Kind ).To (Equal ("Config" ))
167+ }, "10s" , "200ms" ).Should (Succeed ())
168+
169+ pdb , err := getPDB (ctx )
170+ Expect (err ).NotTo (HaveOccurred ())
171+ Expect (k8sClient .Delete (ctx , pdb )).To (Succeed ())
172+
173+ Eventually (func () error {
174+ _ , err := getPDB (ctx )
175+ return err
176+ }, "10s" , "200ms" ).Should (Succeed ())
177+ })
178+
179+ It ("removes the PDB after scaling admission down to one replica" , func (ctx context.Context ) {
180+ updateAdmissionPDBConfig (ctx , 1 , true )
181+
182+ Eventually (func () bool {
183+ _ , err := getPDB (ctx )
184+ return apierrors .IsNotFound (err )
185+ }, "10s" , "200ms" ).Should (BeTrue ())
186+ })
187+
188+ It ("removes the PDB when it is disabled" , func (ctx context.Context ) {
189+ updateAdmissionPDBConfig (ctx , 2 , true )
190+ Eventually (func () error {
191+ _ , err := getPDB (ctx )
192+ return err
193+ }, "10s" , "200ms" ).Should (Succeed ())
194+
195+ updateAdmissionPDBConfig (ctx , 2 , false )
196+ Eventually (func () bool {
197+ _ , err := getPDB (ctx )
198+ return apierrors .IsNotFound (err )
199+ }, "10s" , "200ms" ).Should (BeTrue ())
200+ })
201+ })
202+
203+ Context ("Reconciling the scheduler PodDisruptionBudget" , Ordered , func () {
204+ const (
205+ shardName = "pdb-test"
206+ pdbName = "kai-scheduler-" + shardName
207+ )
208+
209+ updateSchedulerPDBConfig := func (ctx context.Context , replicas int32 , enabled bool ) {
210+ Eventually (func () error {
211+ currentConfig := & kaiv1.Config {}
212+ if err := k8sClient .Get (
213+ ctx ,
214+ types.NamespacedName {Name : constants .DefaultKAIConfigSingeltonInstanceName },
215+ currentConfig ,
216+ ); err != nil {
217+ return err
218+ }
219+ currentConfig .Spec .Scheduler = & kaiv1scheduler.Scheduler {
220+ Replicas : ptr .To (replicas ),
221+ Service : & v1common.Service {
222+ Enabled : ptr .To (true ),
223+ PodDisruptionBudget : & v1common.PodDisruptionBudget {
224+ Enabled : ptr .To (enabled ),
225+ MaxUnavailable : ptr .To (int32 (1 )),
226+ },
227+ },
228+ }
229+ return k8sClient .Update (ctx , currentConfig )
230+ }, "10s" , "200ms" ).Should (Succeed ())
231+ }
232+
233+ getPDB := func (ctx context.Context ) (* policyv1.PodDisruptionBudget , error ) {
234+ pdb := & policyv1.PodDisruptionBudget {}
235+ err := k8sClient .Get (ctx , types.NamespacedName {
236+ Name : pdbName ,
237+ Namespace : kaiConfig .Spec .Namespace ,
238+ }, pdb )
239+ return pdb , err
240+ }
241+
242+ BeforeAll (func (ctx context.Context ) {
243+ updateSchedulerPDBConfig (ctx , 2 , true )
244+ Expect (k8sClient .Create (ctx , & kaiv1.SchedulingShard {
245+ ObjectMeta : metav1.ObjectMeta {Name : shardName },
246+ })).To (Succeed ())
247+ })
248+
249+ AfterAll (func (ctx context.Context ) {
250+ shard := & kaiv1.SchedulingShard {}
251+ err := k8sClient .Get (ctx , types.NamespacedName {Name : shardName }, shard )
252+ if err == nil {
253+ Expect (k8sClient .Delete (ctx , shard )).To (Succeed ())
254+ } else {
255+ Expect (apierrors .IsNotFound (err )).To (BeTrue ())
256+ }
257+ })
258+
259+ It ("creates and watches one PDB for the shard" , func (ctx context.Context ) {
260+ Eventually (func (g Gomega ) {
261+ pdb , err := getPDB (ctx )
262+ g .Expect (err ).NotTo (HaveOccurred ())
263+ g .Expect (metav1 .GetControllerOf (pdb )).NotTo (BeNil ())
264+ g .Expect (metav1 .GetControllerOf (pdb ).Kind ).To (Equal ("SchedulingShard" ))
265+ g .Expect (metav1 .GetControllerOf (pdb ).Name ).To (Equal (shardName ))
266+ }, "10s" , "200ms" ).Should (Succeed ())
267+
268+ pdb , err := getPDB (ctx )
269+ Expect (err ).NotTo (HaveOccurred ())
270+ Expect (k8sClient .Delete (ctx , pdb )).To (Succeed ())
271+
272+ Eventually (func () error {
273+ _ , err := getPDB (ctx )
274+ return err
275+ }, "10s" , "200ms" ).Should (Succeed ())
276+ })
277+
278+ It ("removes the shard PDB after scheduler scales down" , func (ctx context.Context ) {
279+ updateSchedulerPDBConfig (ctx , 1 , true )
280+
281+ Eventually (func () bool {
282+ _ , err := getPDB (ctx )
283+ return apierrors .IsNotFound (err )
284+ }, "10s" , "200ms" ).Should (BeTrue ())
285+ })
286+ })
287+
119288})
0 commit comments