@@ -360,6 +360,92 @@ var _ = Describe("NodeDisruption controller", func() {
360360 return errorsk8s .IsNotFound (err )
361361 }, timeout , interval ).Should (BeTrue ())
362362 })
363+
364+ It ("runs prepare and ready hooks before doNotGrantBefore while keeping the disruption pending" , func () {
365+ mockBasePath := "/api/v2"
366+
367+ By ("Starting an http server to receive the hook" )
368+ prepareCalledCnt := 0
369+ readyCalledCnt := 0
370+
371+ checkHookFn := func (w http.ResponseWriter , req * http.Request ) {
372+ switch req .URL .Path {
373+ case mockBasePath + "/prepare" :
374+ prepareCalledCnt ++
375+ case mockBasePath + "/ready" :
376+ readyCalledCnt ++
377+ }
378+ Expect (req .Header .Get ("Content-Type" )).Should (Equal ("application/json" ))
379+ w .WriteHeader (http .StatusOK )
380+ }
381+
382+ mockServer := startDummyHTTPServer (checkHookFn )
383+ defer mockServer .Close ()
384+
385+ By ("creating a budget that accepts one disruption" )
386+ adb := & nodedisruptionv1alpha1.ApplicationDisruptionBudget {
387+ TypeMeta : metav1.TypeMeta {
388+ APIVersion : "nodedisruption.criteo.com/v1alpha1" ,
389+ Kind : "ApplicationDisruptionBudget" ,
390+ },
391+ ObjectMeta : metav1.ObjectMeta {
392+ Name : "test-do-not-grant-before" ,
393+ Namespace : "default" ,
394+ },
395+ Spec : nodedisruptionv1alpha1.ApplicationDisruptionBudgetSpec {
396+ PodSelector : metav1.LabelSelector {MatchLabels : podLabels },
397+ MaxDisruptions : 1 ,
398+ HookV2BasePath : nodedisruptionv1alpha1.HookSpec {
399+ URL : mockServer .URL + mockBasePath ,
400+ },
401+ },
402+ }
403+ Expect (k8sClient .Create (ctx , adb )).Should (Succeed ())
404+
405+ By ("checking the ApplicationDisruptionBudget is synchronized" )
406+ ADBLookupKey := types.NamespacedName {Name : "test-do-not-grant-before" , Namespace : "default" }
407+ createdADB := & nodedisruptionv1alpha1.ApplicationDisruptionBudget {}
408+ Eventually (func () []string {
409+ err := k8sClient .Get (ctx , ADBLookupKey , createdADB )
410+ Expect (err ).Should (Succeed ())
411+ return createdADB .Status .WatchedNodes
412+ }, timeout , interval ).Should (Equal ([]string {"node1" }))
413+
414+ By ("creating a new NodeDisruption with doNotGrantBefore in the future" )
415+ doNotGrantBefore := metav1 .NewTime (time .Now ().Add (time .Hour ).Truncate (time .Second ))
416+ disruption := & nodedisruptionv1alpha1.NodeDisruption {
417+ TypeMeta : metav1.TypeMeta {
418+ APIVersion : "nodedisruption.criteo.com/v1alpha1" ,
419+ Kind : "NodeDisruption" ,
420+ },
421+ ObjectMeta : metav1.ObjectMeta {
422+ Name : NDName ,
423+ Namespace : NDNamespace ,
424+ },
425+ Spec : nodedisruptionv1alpha1.NodeDisruptionSpec {
426+ NodeSelector : metav1.LabelSelector {MatchLabels : nodeLabels1 },
427+ Type : "maintenance" ,
428+ DoNotGrantBefore : doNotGrantBefore ,
429+ },
430+ }
431+ Expect (k8sClient .Create (ctx , disruption .DeepCopy ())).Should (Succeed ())
432+
433+ NDLookupKey := types.NamespacedName {Name : NDName , Namespace : NDNamespace }
434+ createdDisruption := & nodedisruptionv1alpha1.NodeDisruption {}
435+
436+ By ("checking the NodeDisruption stays pending after the hooks have completed" )
437+ Eventually (func (g Gomega ) {
438+ err := k8sClient .Get (ctx , NDLookupKey , createdDisruption )
439+ g .Expect (err ).ToNot (HaveOccurred ())
440+ g .Expect (createdDisruption .Status .State ).To (Equal (nodedisruptionv1alpha1 .Pending ))
441+ g .Expect (createdDisruption .Status .NextRetryDate .Time ).To (Equal (doNotGrantBefore .Time ))
442+ g .Expect (createdDisruption .Status .DisruptedDisruptionBudgets ).To (HaveLen (1 ))
443+ g .Expect (createdDisruption .Status .DisruptedDisruptionBudgets [0 ].Preparing ).To (BeTrue ())
444+ g .Expect (createdDisruption .Status .DisruptedDisruptionBudgets [0 ].Ok ).To (BeTrue ())
445+ g .Expect (prepareCalledCnt ).To (Equal (1 ))
446+ g .Expect (readyCalledCnt ).To (Equal (1 ))
447+ }, timeout , interval ).Should (Succeed ())
448+ })
363449 })
364450
365451 When ("there are no budgets in the cluster" , func () {
0 commit comments