@@ -20,6 +20,7 @@ SPDX-License-Identifier: Apache-2.0
2020package client
2121
2222import (
23+ "context"
2324 "time"
2425
2526 corev1 "k8s.io/api/core/v1"
@@ -59,6 +60,7 @@ var _ = Describe("ExtendedClient Get", func() {
5960 extendedClient * ExtendedClient
6061 secretInClient * corev1.Secret
6162 objectStore * barmancloudv1.ObjectStore
63+ cancelCtx context.CancelFunc
6264 )
6365
6466 BeforeEach (func () {
@@ -79,7 +81,14 @@ var _ = Describe("ExtendedClient Get", func() {
7981 baseClient := fake .NewClientBuilder ().
8082 WithScheme (scheme ).
8183 WithObjects (secretInClient , objectStore ).Build ()
82- extendedClient = NewExtendedClient (baseClient ).(* ExtendedClient )
84+ ctx , cancel := context .WithCancel (context .Background ())
85+ cancelCtx = cancel
86+ extendedClient = NewExtendedClient (ctx , baseClient ).(* ExtendedClient )
87+ })
88+
89+ AfterEach (func () {
90+ // Cancel the context to stop the cleanup routine
91+ cancelCtx ()
8392 })
8493
8594 It ("returns secret from cache if not expired" , func (ctx SpecContext ) {
@@ -164,3 +173,139 @@ var _ = Describe("ExtendedClient Get", func() {
164173 Expect (objectStore .GetResourceVersion ()).To (Equal ("from cache" ))
165174 })
166175})
176+
177+ var _ = Describe ("ExtendedClient Cache Cleanup" , func () {
178+ var (
179+ extendedClient * ExtendedClient
180+ cancelCtx context.CancelFunc
181+ )
182+
183+ BeforeEach (func () {
184+ baseClient := fake .NewClientBuilder ().
185+ WithScheme (scheme ).
186+ Build ()
187+ ctx , cancel := context .WithCancel (context .Background ())
188+ cancelCtx = cancel
189+ extendedClient = NewExtendedClient (ctx , baseClient ).(* ExtendedClient )
190+ })
191+
192+ AfterEach (func () {
193+ cancelCtx ()
194+ })
195+
196+ It ("cleans up expired entries" , func (ctx SpecContext ) {
197+ // Add some expired entries
198+ expiredSecret1 := & corev1.Secret {
199+ ObjectMeta : metav1.ObjectMeta {
200+ Namespace : "default" ,
201+ Name : "expired-secret-1" ,
202+ },
203+ }
204+ expiredSecret2 := & corev1.Secret {
205+ ObjectMeta : metav1.ObjectMeta {
206+ Namespace : "default" ,
207+ Name : "expired-secret-2" ,
208+ },
209+ }
210+ validSecret := & corev1.Secret {
211+ ObjectMeta : metav1.ObjectMeta {
212+ Namespace : "default" ,
213+ Name : "valid-secret" ,
214+ },
215+ }
216+
217+ // Add expired entries (2 minutes ago)
218+ addToCache (extendedClient , expiredSecret1 , time .Now ().Add (- 2 * time .Minute ).Unix ())
219+ addToCache (extendedClient , expiredSecret2 , time .Now ().Add (- 2 * time .Minute ).Unix ())
220+ // Add valid entry (just now)
221+ addToCache (extendedClient , validSecret , time .Now ().Unix ())
222+
223+ Expect (extendedClient .cachedObjects ).To (HaveLen (3 ))
224+
225+ // Trigger cleanup
226+ extendedClient .cleanupExpiredEntries (ctx )
227+
228+ // Only the valid entry should remain
229+ Expect (extendedClient .cachedObjects ).To (HaveLen (1 ))
230+ Expect (extendedClient .cachedObjects [0 ].entry .GetName ()).To (Equal ("valid-secret" ))
231+ })
232+
233+ It ("does nothing when all entries are valid" , func (ctx SpecContext ) {
234+ validSecret1 := & corev1.Secret {
235+ ObjectMeta : metav1.ObjectMeta {
236+ Namespace : "default" ,
237+ Name : "valid-secret-1" ,
238+ },
239+ }
240+ validSecret2 := & corev1.Secret {
241+ ObjectMeta : metav1.ObjectMeta {
242+ Namespace : "default" ,
243+ Name : "valid-secret-2" ,
244+ },
245+ }
246+
247+ addToCache (extendedClient , validSecret1 , time .Now ().Unix ())
248+ addToCache (extendedClient , validSecret2 , time .Now ().Unix ())
249+
250+ Expect (extendedClient .cachedObjects ).To (HaveLen (2 ))
251+
252+ // Trigger cleanup
253+ extendedClient .cleanupExpiredEntries (ctx )
254+
255+ // Both entries should remain
256+ Expect (extendedClient .cachedObjects ).To (HaveLen (2 ))
257+ })
258+
259+ It ("does nothing when cache is empty" , func (ctx SpecContext ) {
260+ Expect (extendedClient .cachedObjects ).To (BeEmpty ())
261+
262+ // Trigger cleanup
263+ extendedClient .cleanupExpiredEntries (ctx )
264+
265+ Expect (extendedClient .cachedObjects ).To (BeEmpty ())
266+ })
267+
268+ It ("removes all entries when all are expired" , func (ctx SpecContext ) {
269+ expiredSecret1 := & corev1.Secret {
270+ ObjectMeta : metav1.ObjectMeta {
271+ Namespace : "default" ,
272+ Name : "expired-secret-1" ,
273+ },
274+ }
275+ expiredSecret2 := & corev1.Secret {
276+ ObjectMeta : metav1.ObjectMeta {
277+ Namespace : "default" ,
278+ Name : "expired-secret-2" ,
279+ },
280+ }
281+
282+ addToCache (extendedClient , expiredSecret1 , time .Now ().Add (- 2 * time .Minute ).Unix ())
283+ addToCache (extendedClient , expiredSecret2 , time .Now ().Add (- 2 * time .Minute ).Unix ())
284+
285+ Expect (extendedClient .cachedObjects ).To (HaveLen (2 ))
286+
287+ // Trigger cleanup
288+ extendedClient .cleanupExpiredEntries (ctx )
289+
290+ Expect (extendedClient .cachedObjects ).To (BeEmpty ())
291+ })
292+
293+ It ("stops cleanup routine when context is cancelled" , func () {
294+ // Create a new client with a short cleanup interval for testing
295+ baseClient := fake .NewClientBuilder ().
296+ WithScheme (scheme ).
297+ Build ()
298+ ctx , cancel := context .WithCancel (context .Background ())
299+ ec := NewExtendedClient (ctx , baseClient ).(* ExtendedClient )
300+ ec .cleanupInterval = 10 * time .Millisecond
301+
302+ // Cancel the context immediately
303+ cancel ()
304+
305+ // Give the goroutine time to stop
306+ time .Sleep (50 * time .Millisecond )
307+
308+ // The goroutine should have stopped gracefully (no panic or hanging)
309+ // This test mainly verifies the cleanup routine respects context cancellation
310+ })
311+ })
0 commit comments