@@ -14,6 +14,7 @@ import (
14
14
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15
15
"k8s.io/apimachinery/pkg/labels"
16
16
"k8s.io/apimachinery/pkg/runtime"
17
+ "k8s.io/apimachinery/pkg/util/clock"
17
18
"k8s.io/apiserver/pkg/authentication/authenticator"
18
19
"k8s.io/apiserver/pkg/authentication/user"
19
20
clienttesting "k8s.io/client-go/testing"
@@ -486,15 +487,34 @@ func (f fakeOAuthClientLister) Get(name string) (*oapi.OAuthClient, error) {
486
487
}
487
488
488
489
func (f fakeOAuthClientLister ) List (selector labels.Selector ) ([]* oapi.OAuthClient , error ) {
489
- var list []* oapi.OAuthClient
490
- ret , _ := f .clients .List (metav1.ListOptions {})
491
- for i := range ret .Items {
492
- list = append (list , & ret .Items [i ])
493
- }
494
- return list , nil
490
+ panic ("not used" )
491
+ }
492
+
493
+ type fakeTicker struct {
494
+ clock * clock.FakeClock
495
+ ch <- chan time.Time
496
+ }
497
+
498
+ func (t * fakeTicker ) Now () time.Time {
499
+ return t .clock .Now ()
500
+ }
501
+
502
+ func (t * fakeTicker ) C () <- chan time.Time {
503
+ return t .ch
504
+ }
505
+
506
+ func (t * fakeTicker ) Stop () {}
507
+
508
+ func (t * fakeTicker ) NewTicker (d time.Duration ) {
509
+ t .ch = t .clock .Tick (d )
495
510
}
496
511
497
- func checkToken (t * testing.T , name string , authf authenticator.Token , tokens oauthclient.OAuthAccessTokenInterface , present bool ) {
512
+ func (t * fakeTicker ) Sleep (d time.Duration ) {
513
+ t .clock .Sleep (d )
514
+ }
515
+
516
+ func checkToken (t * testing.T , name string , authf authenticator.Token , tokens oauthclient.OAuthAccessTokenInterface , current * fakeTicker , present bool ) {
517
+ t .Helper ()
498
518
userInfo , found , err := authf .AuthenticateToken (name )
499
519
if present {
500
520
if ! found {
@@ -508,9 +528,12 @@ func checkToken(t *testing.T, name string, authf authenticator.Token, tokens oau
508
528
}
509
529
} else {
510
530
if found {
511
- token , _ := tokens .Get (name , metav1.GetOptions {})
531
+ token , tokenErr := tokens .Get (name , metav1.GetOptions {})
532
+ if tokenErr != nil {
533
+ t .Fatal (tokenErr )
534
+ }
512
535
t .Errorf ("Found token (created=%s, timeout=%di, now=%s), but it should be gone!" ,
513
- token .CreationTimestamp , token .InactivityTimeoutSeconds , time .Now ())
536
+ token .CreationTimestamp , token .InactivityTimeoutSeconds , current .Now ())
514
537
}
515
538
if err != errTimedout {
516
539
t .Errorf ("Unexpected error checking absence of token %s: %v" , name , err )
@@ -521,13 +544,24 @@ func checkToken(t *testing.T, name string, authf authenticator.Token, tokens oau
521
544
}
522
545
}
523
546
547
+ func wait (t * testing.T , c chan struct {}) {
548
+ t .Helper ()
549
+ select {
550
+ case <- c :
551
+ case <- time .After (30 * time .Second ):
552
+ t .Fatal ("failed to see channel event" )
553
+ }
554
+ }
555
+
524
556
func TestAuthenticateTokenTimeout (t * testing.T ) {
525
557
stopCh := make (chan struct {})
526
558
defer close (stopCh )
527
559
560
+ testClock := & fakeTicker {clock : clock .NewFakeClock (time.Time {})}
561
+
528
562
defaultTimeout := int32 (30 ) // 30 seconds
529
- clientTimeout := int32 (15 ) // 15 seconds so flush -> 5 seconds
530
- minTimeout := int32 (10 ) // 10 seconds
563
+ clientTimeout := int32 (15 ) // 15 seconds
564
+ minTimeout := int32 (10 ) // 10 seconds -> 10/3 = a tick per 3 seconds
531
565
532
566
testClient := oapi.OAuthClient {
533
567
ObjectMeta : metav1.ObjectMeta {Name : "testClient" },
@@ -541,31 +575,31 @@ func TestAuthenticateTokenTimeout(t *testing.T) {
541
575
ObjectMeta : metav1.ObjectMeta {Name : "slowClient" },
542
576
}
543
577
testToken := oapi.OAuthAccessToken {
544
- ObjectMeta : metav1.ObjectMeta {Name : "testToken" , CreationTimestamp : metav1.Time {Time : time .Now ()}},
578
+ ObjectMeta : metav1.ObjectMeta {Name : "testToken" , CreationTimestamp : metav1.Time {Time : testClock .Now ()}},
545
579
ClientName : "testClient" ,
546
580
ExpiresIn : 600 , // 10 minutes
547
581
UserName : "foo" ,
548
582
UserUID : string ("bar" ),
549
583
InactivityTimeoutSeconds : clientTimeout ,
550
584
}
551
585
quickToken := oapi.OAuthAccessToken {
552
- ObjectMeta : metav1.ObjectMeta {Name : "quickToken" , CreationTimestamp : metav1.Time {Time : time .Now ()}},
586
+ ObjectMeta : metav1.ObjectMeta {Name : "quickToken" , CreationTimestamp : metav1.Time {Time : testClock .Now ()}},
553
587
ClientName : "quickClient" ,
554
588
ExpiresIn : 600 , // 10 minutes
555
589
UserName : "foo" ,
556
590
UserUID : string ("bar" ),
557
591
InactivityTimeoutSeconds : minTimeout ,
558
592
}
559
593
slowToken := oapi.OAuthAccessToken {
560
- ObjectMeta : metav1.ObjectMeta {Name : "slowToken" , CreationTimestamp : metav1.Time {Time : time .Now ()}},
594
+ ObjectMeta : metav1.ObjectMeta {Name : "slowToken" , CreationTimestamp : metav1.Time {Time : testClock .Now ()}},
561
595
ClientName : "slowClient" ,
562
596
ExpiresIn : 600 , // 10 minutes
563
597
UserName : "foo" ,
564
598
UserUID : string ("bar" ),
565
599
InactivityTimeoutSeconds : defaultTimeout ,
566
600
}
567
601
emergToken := oapi.OAuthAccessToken {
568
- ObjectMeta : metav1.ObjectMeta {Name : "emergToken" , CreationTimestamp : metav1.Time {Time : time .Now ()}},
602
+ ObjectMeta : metav1.ObjectMeta {Name : "emergToken" , CreationTimestamp : metav1.Time {Time : testClock .Now ()}},
569
603
ClientName : "quickClient" ,
570
604
ExpiresIn : 600 , // 10 minutes
571
605
UserName : "foo" ,
@@ -580,34 +614,77 @@ func TestAuthenticateTokenTimeout(t *testing.T) {
580
614
lister := & fakeOAuthClientLister {
581
615
clients : oauthClients ,
582
616
}
617
+
583
618
timeouts := NewTimeoutValidator (accessTokenGetter , lister , defaultTimeout , minTimeout )
619
+
620
+ // inject fake clock, which has some interesting properties
621
+ // 1. A sleep will cause at most one ticker event, regardless of how long the sleep was
622
+ // 2. The clock will hold one tick event and will drop the next one if something does not consume it first
623
+ timeouts .ticker = testClock
624
+
625
+ // decorate flush
626
+ // The fake clock 1. and 2. require that we issue a wait(t, timeoutsSync) after each testClock.Sleep that causes a tick
627
+ originalFlush := timeouts .flushHandler
628
+ timeoutsSync := make (chan struct {})
629
+ timeouts .flushHandler = func (flushHorizon time.Time ) {
630
+ originalFlush (flushHorizon )
631
+ timeoutsSync <- struct {}{} // signal that flush is complete so we never race against it
632
+ }
633
+
634
+ // decorate putToken
635
+ // We must issue a wait(t, putTokenSync) after each call to checkToken that should be successful
636
+ originalPutToken := timeouts .putTokenHandler
637
+ putTokenSync := make (chan struct {})
638
+ timeouts .putTokenHandler = func (td * tokenData ) {
639
+ originalPutToken (td )
640
+ putTokenSync <- struct {}{} // signal that putToken is complete so we never race against it
641
+ }
642
+
643
+ // add some padding to all sleep invocations to make sure we are not failing on any boundary values
644
+ buffer := time .Nanosecond
645
+
584
646
tokenAuthenticator := NewTokenAuthenticator (accessTokenGetter , userRegistry , identitymapper.NoopGroupMapper {}, timeouts )
585
647
586
648
go timeouts .Run (stopCh )
587
649
588
- // wait to see that the other thread has updated the timeouts
589
- time .Sleep (1 * time .Second )
590
-
591
- // TIME: 1 seconds have passed here
650
+ // TIME: 0 seconds have passed here
592
651
593
652
// first time should succeed for all
594
- checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , true )
595
- checkToken (t , "quickToken" , tokenAuthenticator , accessTokenGetter , true )
596
- checkToken (t , "slowToken" , tokenAuthenticator , accessTokenGetter , true )
653
+ checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
654
+ wait (t , putTokenSync )
655
+
656
+ checkToken (t , "quickToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
657
+ wait (t , putTokenSync )
658
+
659
+ wait (t , timeoutsSync ) // from emergency flush because quickToken has a short enough timeout
660
+
661
+ checkToken (t , "slowToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
662
+ wait (t , putTokenSync )
663
+
597
664
// this should cause an emergency flush, if not the next auth will fail,
598
665
// as the token will be timed out
599
- checkToken (t , "emergToken" , tokenAuthenticator , accessTokenGetter , true )
666
+ checkToken (t , "emergToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
667
+ wait (t , putTokenSync )
668
+
669
+ wait (t , timeoutsSync ) // from emergency flush because emergToken has a super short timeout
600
670
601
- // wait 5 seconds
602
- time .Sleep (5 * time .Second )
671
+ // wait 6 seconds
672
+ testClock .Sleep (5 * time .Second + buffer )
673
+
674
+ // a tick happens every 3 seconds
675
+ wait (t , timeoutsSync )
603
676
604
677
// TIME: 6th second
605
678
606
679
// See if emergency flush happened
607
- checkToken (t , "emergToken" , tokenAuthenticator , accessTokenGetter , true )
680
+ checkToken (t , "emergToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
681
+ wait (t , putTokenSync )
682
+
683
+ wait (t , timeoutsSync ) // from emergency flush because emergToken has a super short timeout
608
684
609
685
// wait for timeout (minTimeout + 1 - the previously waited 6 seconds)
610
- time .Sleep (time .Duration (minTimeout - 5 ) * time .Second )
686
+ testClock .Sleep (time .Duration (minTimeout - 5 )* time .Second + buffer )
687
+ wait (t , timeoutsSync )
611
688
612
689
// TIME: 11th second
613
690
@@ -625,21 +702,34 @@ func TestAuthenticateTokenTimeout(t *testing.T) {
625
702
}
626
703
}
627
704
628
- // this should fail
629
- checkToken (t , "quickToken" , tokenAuthenticator , accessTokenGetter , false )
705
+ // this should fail, thus no call to wait(t, putTokenSync)
706
+ checkToken (t , "quickToken" , tokenAuthenticator , accessTokenGetter , testClock , false )
707
+
630
708
// while this should get updated
631
- checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , true )
709
+ checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
710
+ wait (t , putTokenSync )
711
+
712
+ wait (t , timeoutsSync )
632
713
633
714
// wait for timeout
634
- time .Sleep (time .Duration (clientTimeout + 1 ) * time .Second )
715
+ testClock .Sleep (time .Duration (clientTimeout + 1 )* time .Second + buffer )
716
+
717
+ // 16 seconds equals 5 more flushes, but the fake clock will only tick once during this time
718
+ wait (t , timeoutsSync )
635
719
636
720
// TIME: 27th second
637
721
638
722
// this should get updated
639
- checkToken (t , "slowToken" , tokenAuthenticator , accessTokenGetter , true )
723
+ checkToken (t , "slowToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
724
+ wait (t , putTokenSync )
725
+
726
+ wait (t , timeoutsSync )
640
727
641
728
// while this should not fail
642
- checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , true )
729
+ checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
730
+ wait (t , putTokenSync )
731
+
732
+ wait (t , timeoutsSync )
643
733
// and should be updated to last at least till the 31st second
644
734
token , err := accessTokenGetter .Get ("testToken" , metav1.GetOptions {})
645
735
if err != nil {
@@ -663,10 +753,15 @@ func TestAuthenticateTokenTimeout(t *testing.T) {
663
753
}
664
754
665
755
// and wait until test token should time out, and has been flushed for sure
666
- time .Sleep (time .Duration (minTimeout ) * time .Second )
756
+ testClock .Sleep (time .Duration (minTimeout )* time .Second + buffer )
757
+ wait (t , timeoutsSync )
667
758
668
759
// while this should not fail
669
- checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , true )
760
+ checkToken (t , "testToken" , tokenAuthenticator , accessTokenGetter , testClock , true )
761
+ wait (t , putTokenSync )
762
+
763
+ wait (t , timeoutsSync )
764
+
670
765
// and should be updated to have a ZERO timeout
671
766
token , err = accessTokenGetter .Get ("testToken" , metav1.GetOptions {})
672
767
if err != nil {
0 commit comments