2929import org .junit .Before ;
3030import org .mockito .Mockito ;
3131
32+ import java .time .Clock ;
33+ import java .time .Duration ;
34+
3235import static org .elasticsearch .cluster .metadata .Metadata .EMPTY_METADATA ;
3336import static org .elasticsearch .persistent .PersistentTasksExecutor .NO_NODE_FOUND ;
3437import static org .elasticsearch .test .ClusterServiceUtils .createClusterService ;
@@ -86,7 +89,8 @@ public void testMultipleCallsToStart_OnlyRegistersOnce() {
8689 mock (Client .class ),
8790 createMockCCMFeature (false ),
8891 createMockCCMService (false )
89- )
92+ ),
93+ Clock .systemUTC ()
9094 );
9195 executor .startAndImmediatelyCreateTask ();
9296 executor .startAndImmediatelyCreateTask ();
@@ -117,7 +121,8 @@ public void testStartLazy_OnlyRegistersOnce_NeverCallsPersistentTaskService() {
117121 mock (Client .class ),
118122 createMockCCMFeature (false ),
119123 createMockCCMService (false )
120- )
124+ ),
125+ Clock .systemUTC ()
121126 );
122127 executor .startAndLazyCreateTask ();
123128 executor .startAndLazyCreateTask ();
@@ -154,7 +159,8 @@ public void testDoesNotRegisterListener_IfUrlIsEmpty() {
154159 mock (Client .class ),
155160 createMockCCMFeature (false ),
156161 createMockCCMService (false )
157- )
162+ ),
163+ Clock .systemUTC ()
158164 );
159165 executor .startAndImmediatelyCreateTask ();
160166 executor .startAndImmediatelyCreateTask ();
@@ -170,6 +176,17 @@ public void testDoesNotRegisterListener_IfUrlIsEmpty() {
170176 }
171177
172178 public void testMultipleCallsToStart_AndStop () {
179+ var now = Clock .systemUTC ().instant ();
180+ var oneDayInFuture = now .plus (Duration .ofDays (1 ));
181+ var clock = mock (Clock .class );
182+ // The AuthorizationTaskExecutor does these calls:
183+ // 1. Check if the last create task time is expired (first call to instant()),
184+ // this will pass so a call to create the task will occur
185+ // 2. Then it will update the last create task time (second call to instant())
186+ // 3. On the next cluster state change, it will check if the last create task time is expired (third call to instant()),
187+ // we'll return now + 1 day to ensure that it is expired and allows another call to create the task
188+ when (clock .instant ()).thenReturn (now ).thenReturn (now ).thenReturn (oneDayInFuture );
189+
173190 var eisUrl = "abc" ;
174191 var mockClusterService = createMockEmptyClusterService ();
175192 var executor = new AuthorizationTaskExecutor (
@@ -185,7 +202,8 @@ public void testMultipleCallsToStart_AndStop() {
185202 mock (Client .class ),
186203 createMockCCMFeature (false ),
187204 createMockCCMService (false )
188- )
205+ ),
206+ clock
189207 );
190208 executor .startAndImmediatelyCreateTask ();
191209 executor .startAndImmediatelyCreateTask ();
@@ -216,6 +234,62 @@ public void testMultipleCallsToStart_AndStop() {
216234 verify (persistentTasksService , times (2 )).sendClusterRemoveRequest (eq (AuthorizationPoller .TASK_NAME ), any (), any ());
217235 }
218236
237+ public void testMultipleCallsToStart_OnlyCallsSendClusterStartRequestOnce_WhenRateLimited () {
238+ var now = Clock .systemUTC ().instant ();
239+ var clock = mock (Clock .class );
240+ when (clock .instant ()).thenReturn (now );
241+
242+ var eisUrl = "abc" ;
243+ var mockClusterService = createMockEmptyClusterService ();
244+ var executor = new AuthorizationTaskExecutor (
245+ mockClusterService ,
246+ persistentTasksService ,
247+ enabledFeatureServiceMock ,
248+ new AuthorizationPoller .Parameters (
249+ createWithEmptySettings (threadPool ),
250+ mock (ElasticInferenceServiceAuthorizationRequestHandler .class ),
251+ mock (Sender .class ),
252+ ElasticInferenceServiceSettingsTests .create (eisUrl , TimeValue .timeValueMillis (1 ), TimeValue .timeValueMillis (1 ), true ),
253+ mock (ModelRegistry .class ),
254+ mock (Client .class ),
255+ createMockCCMFeature (false ),
256+ createMockCCMService (false )
257+ ),
258+ clock
259+ );
260+ executor .startAndImmediatelyCreateTask ();
261+ executor .startAndImmediatelyCreateTask ();
262+ executor .stop ();
263+ executor .stop ();
264+ verify (mockClusterService , times (1 )).addListener (executor );
265+ verify (persistentTasksService , times (1 )).sendClusterStartRequest (
266+ eq (AuthorizationPoller .TASK_NAME ),
267+ eq (AuthorizationPoller .TASK_NAME ),
268+ eq (AuthorizationTaskParams .INSTANCE ),
269+ any (),
270+ any ()
271+ );
272+ verify (mockClusterService , times (1 )).removeListener (executor );
273+ verify (persistentTasksService , times (1 )).sendClusterRemoveRequest (eq (AuthorizationPoller .TASK_NAME ), any (), any ());
274+
275+ Mockito .clearInvocations (persistentTasksService );
276+ Mockito .clearInvocations (mockClusterService );
277+
278+ executor .startAndImmediatelyCreateTask ();
279+ executor .stop ();
280+ verify (mockClusterService , times (1 )).addListener (executor );
281+ // No additional calls because time hasn't advanced to allow another task creation call
282+ verify (persistentTasksService , never ()).sendClusterStartRequest (
283+ eq (AuthorizationPoller .TASK_NAME ),
284+ eq (AuthorizationPoller .TASK_NAME ),
285+ eq (AuthorizationTaskParams .INSTANCE ),
286+ any (),
287+ any ()
288+ );
289+ verify (mockClusterService , times (1 )).removeListener (executor );
290+ verify (persistentTasksService , times (1 )).sendClusterRemoveRequest (eq (AuthorizationPoller .TASK_NAME ), any (), any ());
291+ }
292+
219293 public void testCallsSendClusterStartRequest_WhenStartIsCalled () {
220294 var eisUrl = "abc" ;
221295 var mockClusterService = createMockEmptyClusterService ();
@@ -232,7 +306,8 @@ public void testCallsSendClusterStartRequest_WhenStartIsCalled() {
232306 mock (Client .class ),
233307 createMockCCMFeature (false ),
234308 createMockCCMService (false )
235- )
309+ ),
310+ Clock .systemUTC ()
236311 );
237312 executor .startAndImmediatelyCreateTask ();
238313
@@ -282,7 +357,8 @@ public void testDoesNotCallSendClusterStartRequest_WhenStartIsCalled_WhenItIsAlr
282357 mock (Client .class ),
283358 createMockCCMFeature (false ),
284359 createMockCCMService (false )
285- )
360+ ),
361+ Clock .systemUTC ()
286362 );
287363 executor .startAndImmediatelyCreateTask ();
288364
@@ -297,6 +373,17 @@ public void testDoesNotCallSendClusterStartRequest_WhenStartIsCalled_WhenItIsAlr
297373 }
298374
299375 public void testCreatesTask_WhenItDoesNotExistOnClusterStateChange () {
376+ var now = Clock .systemUTC ().instant ();
377+ var oneDayInFuture = now .plus (Duration .ofDays (1 ));
378+ var clock = mock (Clock .class );
379+ // The AuthorizationTaskExecutor does these calls:
380+ // 1. Check if the last create task time is expired (first call to instant()),
381+ // this will pass so a call to create the task will occur
382+ // 2. Then it will update the last create task time (second call to instant())
383+ // 3. On the next cluster state change, it will check if the last create task time is expired (third call to instant()),
384+ // we'll return now + 1 day to ensure that it is expired and allows another call to create the task
385+ when (clock .instant ()).thenReturn (now ).thenReturn (now ).thenReturn (oneDayInFuture );
386+
300387 var eisUrl = "abc" ;
301388
302389 var executor = new AuthorizationTaskExecutor (
@@ -312,7 +399,8 @@ public void testCreatesTask_WhenItDoesNotExistOnClusterStateChange() {
312399 mock (Client .class ),
313400 createMockCCMFeature (false ),
314401 createMockCCMService (false )
315- )
402+ ),
403+ clock
316404 );
317405 executor .startAndImmediatelyCreateTask ();
318406
@@ -329,6 +417,9 @@ public void testCreatesTask_WhenItDoesNotExistOnClusterStateChange() {
329417 );
330418
331419 Mockito .clearInvocations (persistentTasksService );
420+ Mockito .clearInvocations (clock );
421+ when (clock .instant ()).thenReturn (oneDayInFuture .plus (Duration .ofDays (1 )));
422+
332423 // Ensure that if the task is gone, it will be recreated.
333424 var listener2 = new PlainActionFuture <Void >();
334425 clusterService .getClusterApplierService ().onNewClusterState ("initialization" , this ::initialState , listener2 );
@@ -369,7 +460,8 @@ public void testDoesNotCreateTask_WhenFeatureIsNotSupported() {
369460 mock (Client .class ),
370461 createMockCCMFeature (false ),
371462 createMockCCMService (false )
372- )
463+ ),
464+ Clock .systemUTC ()
373465 );
374466 executor .startAndImmediatelyCreateTask ();
375467
@@ -400,7 +492,8 @@ public void testDoesNotRegisterClusterStateListener_DoesNotCreateTask_WhenTheEis
400492 mock (Client .class ),
401493 createMockCCMFeature (false ),
402494 createMockCCMService (false )
403- )
495+ ),
496+ Clock .systemUTC ()
404497 );
405498 executor .startAndImmediatelyCreateTask ();
406499
@@ -430,7 +523,8 @@ public void testDoesNotRegisterClusterStateListener_DoesNotCreateTask_WhenTheEis
430523 mock (Client .class ),
431524 createMockCCMFeature (false ),
432525 createMockCCMService (false )
433- )
526+ ),
527+ Clock .systemUTC ()
434528 );
435529 executor .startAndImmediatelyCreateTask ();
436530
@@ -483,7 +577,8 @@ public void testDoesNotCreateTask_OnClusterStateChange_WhenItAlreadyExists() {
483577 mock (Client .class ),
484578 createMockCCMFeature (false ),
485579 createMockCCMService (false )
486- )
580+ ),
581+ Clock .systemUTC ()
487582 );
488583 executor .startAndImmediatelyCreateTask ();
489584
0 commit comments