@@ -378,6 +378,85 @@ var _ = Describe("manger.Manager", func() {
378378
379379 Expect (cm .gracefulShutdownTimeout .Nanoseconds ()).To (Equal (int64 (0 )))
380380 })
381+
382+ It ("should prevent leader election when shutting down a non-elected manager" , func () {
383+ var rl resourcelock.Interface
384+ m1 , err := New (cfg , Options {
385+ LeaderElection : true ,
386+ LeaderElectionNamespace : "default" ,
387+ LeaderElectionID : "test-leader-election-id" ,
388+ newResourceLock : func (config * rest.Config , recorderProvider recorder.Provider , options leaderelection.Options ) (resourcelock.Interface , error ) {
389+ var err error
390+ rl , err = leaderelection .NewResourceLock (config , recorderProvider , options )
391+ return rl , err
392+ },
393+ HealthProbeBindAddress : "0" ,
394+ Metrics : metricsserver.Options {BindAddress : "0" },
395+ PprofBindAddress : "0" ,
396+ })
397+ Expect (err ).ToNot (HaveOccurred ())
398+ Expect (m1 ).ToNot (BeNil ())
399+ Expect (rl .Describe ()).To (Equal ("default/test-leader-election-id" ))
400+
401+ m1cm , ok := m1 .(* controllerManager )
402+ Expect (ok ).To (BeTrue ())
403+ m1cm .onStoppedLeading = func () {}
404+
405+ m2 , err := New (cfg , Options {
406+ LeaderElection : true ,
407+ LeaderElectionNamespace : "default" ,
408+ LeaderElectionID : "test-leader-election-id" ,
409+ newResourceLock : func (config * rest.Config , recorderProvider recorder.Provider , options leaderelection.Options ) (resourcelock.Interface , error ) {
410+ var err error
411+ rl , err = leaderelection .NewResourceLock (config , recorderProvider , options )
412+ return rl , err
413+ },
414+ HealthProbeBindAddress : "0" ,
415+ Metrics : metricsserver.Options {BindAddress : "0" },
416+ PprofBindAddress : "0" ,
417+ })
418+ Expect (err ).ToNot (HaveOccurred ())
419+ Expect (m2 ).ToNot (BeNil ())
420+ Expect (rl .Describe ()).To (Equal ("default/test-leader-election-id" ))
421+
422+ m1done := make (chan struct {})
423+ Expect (m1 .Add (RunnableFunc (func (ctx context.Context ) error {
424+ defer GinkgoRecover ()
425+ close (m1done )
426+ return nil
427+ }))).To (Succeed ())
428+
429+ ctx1 , cancel1 := context .WithCancel (context .Background ())
430+ defer cancel1 ()
431+ go func () {
432+ defer GinkgoRecover ()
433+ Expect (m1 .Elected ()).ShouldNot (BeClosed ())
434+ Expect (m1 .Start (ctx1 )).NotTo (HaveOccurred ())
435+ }()
436+ <- m1 .Elected ()
437+ <- m1done
438+
439+ electionRunnable := & needElection {make (chan struct {})}
440+
441+ Expect (m2 .Add (electionRunnable )).To (Succeed ())
442+
443+ ctx2 , cancel2 := context .WithCancel (context .Background ())
444+ m2done := make (chan struct {})
445+ go func () {
446+ defer GinkgoRecover ()
447+ Expect (m2 .Start (ctx2 )).NotTo (HaveOccurred ())
448+ close (m2done )
449+ }()
450+ Consistently (m2 .Elected ()).ShouldNot (Receive ())
451+
452+ go func () {
453+ defer GinkgoRecover ()
454+ Consistently (electionRunnable .ch ).ShouldNot (Receive ())
455+ }()
456+ cancel2 ()
457+ <- m2done
458+ })
459+
381460 It ("should default ID to controller-runtime if ID is not set" , func () {
382461 var rl resourcelock.Interface
383462 m1 , err := New (cfg , Options {
@@ -1929,3 +2008,16 @@ func (f *fakeDeferredLoader) InjectScheme(scheme *runtime.Scheme) error {
19292008type metricsDefaultServer interface {
19302009 GetBindAddr () string
19312010}
2011+
2012+ type needElection struct {
2013+ ch chan struct {}
2014+ }
2015+
2016+ func (n * needElection ) Start (_ context.Context ) error {
2017+ n .ch <- struct {}{}
2018+ return nil
2019+ }
2020+
2021+ func (n * needElection ) NeedLeaderElection () bool {
2022+ return true
2023+ }
0 commit comments