@@ -28,15 +28,34 @@ import (
28
28
)
29
29
30
30
// ClientCallbackOn is an enumeration of possible client callback events.
31
- type ClientCallbackOn int
31
+ type ClientCallbackOn uint32
32
32
33
33
const (
34
+ NamePrefixVCC = "vso-cc-"
35
+
34
36
// ClientCallbackOnLifetimeWatcherDone is a ClientCallbackOn that handles client
35
37
// lifetime watcher done events.
36
- ClientCallbackOnLifetimeWatcherDone ClientCallbackOn = iota
37
- NamePrefixVCC = "vso-cc-"
38
+ ClientCallbackOnLifetimeWatcherDone ClientCallbackOn = 1 << iota
39
+ // ClientCallbackOnCacheRemoval is a ClientCallbackOn that handles client cache removal events.
40
+ ClientCallbackOnCacheRemoval
38
41
)
39
42
43
+ func (o ClientCallbackOn ) String () string {
44
+ switch o {
45
+ case ClientCallbackOnLifetimeWatcherDone :
46
+ return "LifetimeWatcherDone"
47
+ case ClientCallbackOnCacheRemoval :
48
+ return "CacheRemoval"
49
+ default :
50
+ return "Unknown"
51
+ }
52
+ }
53
+
54
+ type ClientCallbackHandlerRequest struct {
55
+ On ClientCallbackOn
56
+ Client Client
57
+ }
58
+
40
59
// ClientCallback is a function type that takes a context, a Client, and an error as parameters.
41
60
// It is used in the context of a ClientCallbackHandler.
42
61
type ClientCallback func (ctx context.Context , c Client )
@@ -99,7 +118,7 @@ type cachingClientFactory struct {
99
118
pruneStorageOnEvict bool
100
119
ctrlClient ctrlclient.Client
101
120
clientCallbacks []ClientCallbackHandler
102
- callbackHandlerCh chan Client
121
+ callbackHandlerCh chan * ClientCallbackHandlerRequest
103
122
mu sync.RWMutex
104
123
onceDoWatcher sync.Once
105
124
callbackHandlerCancel context.CancelFunc
@@ -182,7 +201,10 @@ func (m *cachingClientFactory) prune(ctx context.Context, client ctrlclient.Clie
182
201
if ! skipCallbacks {
183
202
for _ , c := range pruned {
184
203
// the callback handler will remove the client from the storage
185
- m .callbackHandlerCh <- c
204
+ m .callbackHandlerCh <- & ClientCallbackHandlerRequest {
205
+ On : ClientCallbackOnCacheRemoval ,
206
+ Client : c ,
207
+ }
186
208
}
187
209
} else {
188
210
// for all cache entries pruned, remove the corresponding storage entries.
@@ -404,38 +426,31 @@ func (m *cachingClientFactory) Get(ctx context.Context, client ctrlclient.Client
404
426
if ok {
405
427
// return the Client from the cache if it is still Valid
406
428
tainted = c .Tainted ()
407
- logger .V (consts .LogLevelTrace ).Info ("Got client from cache" , "clientID" , c .ID (), "tainted" , tainted )
408
- if tainted {
409
- // if the Client is tainted, we need to validate its token.
410
- if _ , err := c .Read (ctx , NewReadRequest ("auth/token/lookup-self" , nil )); err == nil {
411
- defer c .Untaint ()
412
- tainted = false
413
- if err := c .Validate (); err == nil {
414
- return namespacedClient (c )
415
- }
429
+ logger .V (consts .LogLevelTrace ).Info ("Got client from cache" ,
430
+ "clientID" , c .ID (), "tainted" , tainted )
431
+ if err := c .Validate (ctx ); err != nil {
432
+ logger .V (consts .LogLevelDebug ).Error (err , "Invalid client" ,
433
+ "tainted" , tainted )
434
+ m .callbackHandlerCh <- & ClientCallbackHandlerRequest {
435
+ On : ClientCallbackOnCacheRemoval ,
436
+ Client : c ,
416
437
}
417
- } else if err := c .Validate (); err == nil {
438
+ } else {
439
+ c .Untaint ()
418
440
return namespacedClient (c )
419
441
}
420
-
421
- logger .V (consts .LogLevelDebug ).Error (err , "Invalid client" ,
422
- "tainted" , tainted )
423
-
424
- // remove the parent Client from the cache in order to prune any of its clones.
425
- m .cache .Remove (cacheKey )
426
442
} else {
427
443
logger .V (consts .LogLevelTrace ).Info ("Client not found in cache" , "cacheKey" , fmt .Sprintf ("%#v" , cacheKey ))
428
- }
429
-
430
- if ! ok && m .storageEnabled () {
431
- // try and restore from Client storage cache, if properly configured to do so.
432
- restored , err := m .restoreClientFromCacheKey (ctx , client , cacheKey )
433
- if restored != nil {
434
- return namespacedClient (restored )
435
- }
444
+ if m .storageEnabled () {
445
+ // try and restore from Client storage cache, if properly configured to do so.
446
+ restored , err := m .restoreClientFromCacheKey (ctx , client , cacheKey )
447
+ if restored != nil {
448
+ return namespacedClient (restored )
449
+ }
436
450
437
- if ! IsStorageEntryNotFoundErr (err ) {
438
- logger .Error (err , "Failed to restore client from storage" )
451
+ if ! IsStorageEntryNotFoundErr (err ) {
452
+ logger .Error (err , "Failed to restore client from storage" )
453
+ }
439
454
}
440
455
}
441
456
@@ -642,7 +657,7 @@ func (m *cachingClientFactory) storageEncryptionClient(ctx context.Context, clie
642
657
// ensure that the cached Vault Client is not expired, and if it is then call storageEncryptionClient() again.
643
658
// This operation should be safe since we are setting m.clientCacheKeyEncrypt to empty string,
644
659
// so there should be no risk of causing a maximum recursion error.
645
- if reason := c .Validate (); reason != nil {
660
+ if reason := c .Validate (ctx ); reason != nil {
646
661
m .logger .V (consts .LogLevelWarning ).Info ("Restored Vault client is invalid, recreating it" ,
647
662
"cacheKey" , m .clientCacheKeyEncrypt , "reason" , reason )
648
663
@@ -677,7 +692,7 @@ func (m *cachingClientFactory) startClientCallbackHandler(ctx context.Context) {
677
692
678
693
go func () {
679
694
if m .callbackHandlerCh == nil {
680
- m .callbackHandlerCh = make (chan Client )
695
+ m .callbackHandlerCh = make (chan * ClientCallbackHandlerRequest )
681
696
}
682
697
defer func () {
683
698
close (m .callbackHandlerCh )
@@ -689,16 +704,20 @@ func (m *cachingClientFactory) startClientCallbackHandler(ctx context.Context) {
689
704
case <- callbackCtx .Done ():
690
705
logger .Info ("Client callback handler done" )
691
706
return
692
- case c , stillOpen := <- m .callbackHandlerCh :
707
+ case req , stillOpen := <- m .callbackHandlerCh :
693
708
if ! stillOpen {
694
709
logger .Info ("Client callback handler channel closed" )
695
710
return
696
711
}
697
- if c . IsClone () {
712
+ if req == nil {
698
713
continue
699
714
}
700
715
701
- cacheKey , err := c .GetCacheKey ()
716
+ if req .Client .IsClone () {
717
+ continue
718
+ }
719
+
720
+ cacheKey , err := req .Client .GetCacheKey ()
702
721
if err != nil {
703
722
logger .Error (err , "Invalid client, client callbacks not executed" ,
704
723
"cacheKey" , cacheKey )
@@ -708,30 +727,62 @@ func (m *cachingClientFactory) startClientCallbackHandler(ctx context.Context) {
708
727
// remove the client from the cache, it will be recreated when a reconciler
709
728
// requests it.
710
729
logger .V (consts .LogLevelDebug ).Info ("Removing client from cache" , "cacheKey" , cacheKey )
711
- m .cache .Remove (cacheKey )
712
- if m .storageEnabled () {
713
- if _ , err := m .pruneStorage (ctx , m .ctrlClient , cacheKey ); err != nil {
714
- logger .Info ("Warning: failed to prune storage" , "cacheKey" , cacheKey )
730
+ if req .On & ClientCallbackOnLifetimeWatcherDone != 0 {
731
+ m .cache .Remove (cacheKey )
732
+ if m .storageEnabled () {
733
+ if _ , err := m .pruneStorage (ctx , m .ctrlClient , cacheKey ); err != nil {
734
+ logger .Info ("Warning: failed to prune storage" , "cacheKey" , cacheKey )
735
+ }
715
736
}
716
737
}
717
738
718
- for idx , cbReq := range m .clientCallbacks {
719
- if cbReq .On != ClientCallbackOnLifetimeWatcherDone {
720
- continue
721
- }
722
-
723
- logger .Info ("Calling client callback on lifetime watcher done" ,
724
- "index" , idx , "cacheKey" , cacheKey , "clientID" , c .ID ())
725
- // call in a go routine to avoid blocking the channel
726
- go func (cbReq ClientCallbackHandler ) {
727
- cbReq .Callback (ctx , c )
728
- }(cbReq )
729
- }
739
+ m .callClientCallbacks (ctx , req .Client , req .On , false )
730
740
}
731
741
}
732
742
}()
733
743
}
734
744
745
+ // callClientCallbacks calls all registered client callbacks for the specified
746
+ // event. If wait is true, it will block until all callbacks have been executed.
747
+ // Note: wait is only for testing purposes.
748
+ func (m * cachingClientFactory ) callClientCallbacks (ctx context.Context , c Client , on ClientCallbackOn , wait bool ) {
749
+ logger := log .FromContext (ctx ).WithName ("callClientCallbacks" )
750
+
751
+ var cbs []ClientCallbackHandler
752
+ for _ , cbReq := range m .clientCallbacks {
753
+ x := on & cbReq .On
754
+ if x != 0 {
755
+ cbs = append (cbs , cbReq )
756
+ continue
757
+ }
758
+ }
759
+
760
+ if len (cbs ) == 0 {
761
+ return
762
+ }
763
+
764
+ var wg sync.WaitGroup
765
+ if wait {
766
+ wg .Add (len (cbs ))
767
+ }
768
+
769
+ for idx , cbReq := range cbs {
770
+ logger .Info ("Calling client callback" ,
771
+ "index" , idx , "clientID" , c .ID (), "on" , on )
772
+ // call in a go routine to avoid blocking the channel
773
+ go func (cbReq ClientCallbackHandler ) {
774
+ if wait {
775
+ defer wg .Done ()
776
+ }
777
+ cbReq .Callback (ctx , c )
778
+ }(cbReq )
779
+ }
780
+
781
+ if wait {
782
+ wg .Wait ()
783
+ }
784
+ }
785
+
735
786
// NewCachingClientFactory returns a CachingClientFactory with ClientCache initialized.
736
787
// The ClientCache's onEvictCallback is registered with the factory's onClientEvict(),
737
788
// to ensure any evictions are handled by the factory (this is very important).
@@ -741,7 +792,7 @@ func NewCachingClientFactory(ctx context.Context, client ctrlclient.Client, cach
741
792
recorder : config .Recorder ,
742
793
persist : config .Persist ,
743
794
ctrlClient : client ,
744
- callbackHandlerCh : make (chan Client ),
795
+ callbackHandlerCh : make (chan * ClientCallbackHandlerRequest ),
745
796
encryptionRequired : config .StorageConfig .EnforceEncryption ,
746
797
clientLocks : make (map [ClientCacheKey ]* sync.RWMutex , config .ClientCacheSize ),
747
798
logger : zap .New ().WithName ("clientCacheFactory" ).WithValues (
0 commit comments