8
8
resetRunnersCaches ,
9
9
terminateRunner ,
10
10
tryReuseRunner ,
11
- terminateRunners ,
12
11
} from './runners' ;
13
12
import { RunnerInfo } from './utils' ;
14
13
import { ScaleUpMetrics } from './metrics' ;
@@ -327,11 +326,10 @@ describe('listSSMParameters', () => {
327
326
} ) ;
328
327
} ) ;
329
328
330
- describe ( 'terminateRunners ' , ( ) => {
329
+ describe ( 'terminateRunner ' , ( ) => {
331
330
beforeEach ( ( ) => {
332
331
mockSSMdescribeParametersRet . mockClear ( ) ;
333
332
mockEC2 . terminateInstances . mockClear ( ) ;
334
- mockSSM . deleteParameter . mockClear ( ) ;
335
333
const config = {
336
334
environment : 'gi-ci' ,
337
335
minimumRunningTimeInMinutes : 45 ,
@@ -341,195 +339,66 @@ describe('terminateRunners', () => {
341
339
resetRunnersCaches ( ) ;
342
340
} ) ;
343
341
344
- it ( 'terminates multiple runners in same region successfully' , async ( ) => {
345
- const runners : RunnerInfo [ ] = [
346
- {
347
- awsRegion : 'us-east-1' ,
348
- instanceId : 'i-1234' ,
349
- environment : 'gi-ci' ,
350
- } ,
351
- {
352
- awsRegion : 'us-east-1' ,
353
- instanceId : 'i-5678' ,
354
- environment : 'gi-ci' ,
355
- } ,
356
- ] ;
357
-
342
+ it ( 'calls terminateInstances' , async ( ) => {
343
+ const runner : RunnerInfo = {
344
+ awsRegion : Config . Instance . awsRegion ,
345
+ instanceId : 'i-1234' ,
346
+ environment : 'gi-ci' ,
347
+ } ;
358
348
mockSSMdescribeParametersRet . mockResolvedValueOnce ( {
359
- Parameters : runners
360
- . map ( ( runner ) => getParameterNameForRunner ( runner . environment as string , runner . instanceId ) )
361
- . map ( ( s ) => ( { Name : s } ) ) ,
349
+ Parameters : [ getParameterNameForRunner ( runner . environment as string , runner . instanceId ) ] . map ( ( s ) => {
350
+ return { Name : s } ;
351
+ } ) ,
362
352
} ) ;
353
+ await terminateRunner ( runner , metrics ) ;
363
354
364
- await terminateRunners ( runners , metrics ) ;
365
-
366
- expect ( mockEC2 . terminateInstances ) . toBeCalledTimes ( 1 ) ;
367
355
expect ( mockEC2 . terminateInstances ) . toBeCalledWith ( {
368
- InstanceIds : [ 'i-1234' , 'i-5678' ] ,
356
+ InstanceIds : [ runner . instanceId ] ,
369
357
} ) ;
370
358
expect ( mockSSM . describeParameters ) . toBeCalledTimes ( 1 ) ;
371
- expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 2 ) ;
372
- } ) ;
373
-
374
- it ( 'terminates runners across multiple regions' , async ( ) => {
375
- const runners : RunnerInfo [ ] = [
376
- {
377
- awsRegion : 'us-east-1' ,
378
- instanceId : 'i-1234' ,
379
- environment : 'gi-ci' ,
380
- } ,
381
- {
382
- awsRegion : 'us-west-2' ,
383
- instanceId : 'i-5678' ,
384
- environment : 'gi-ci' ,
385
- } ,
386
- ] ;
387
-
388
- mockSSMdescribeParametersRet . mockResolvedValue ( {
389
- Parameters : [ { Name : 'gi-ci-i-1234' } , { Name : 'gi-ci-i-5678' } ] ,
390
- } ) ;
391
-
392
- await terminateRunners ( runners , metrics ) ;
393
-
394
- expect ( mockEC2 . terminateInstances ) . toBeCalledTimes ( 2 ) ;
395
- expect ( mockEC2 . terminateInstances ) . toHaveBeenNthCalledWith ( 1 , {
396
- InstanceIds : [ 'i-1234' ] ,
397
- } ) ;
398
- expect ( mockEC2 . terminateInstances ) . toHaveBeenNthCalledWith ( 2 , {
399
- InstanceIds : [ 'i-5678' ] ,
359
+ expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 1 ) ;
360
+ expect ( mockSSM . deleteParameter ) . toBeCalledWith ( {
361
+ Name : getParameterNameForRunner ( runner . environment as string , runner . instanceId ) ,
400
362
} ) ;
401
- expect ( mockSSM . describeParameters ) . toBeCalledTimes ( 2 ) ;
402
- expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 2 ) ;
403
363
} ) ;
404
364
405
- it ( 'handles partial failure - terminates some runners but fails on others' , async ( ) => {
406
- const runners : RunnerInfo [ ] = [
407
- {
408
- awsRegion : 'us-east-1' ,
409
- instanceId : 'i-1234' ,
410
- environment : 'gi-ci' ,
411
- } ,
412
- {
413
- awsRegion : 'us-east-1' ,
414
- instanceId : 'i-5678' ,
415
- environment : 'gi-ci' ,
416
- } ,
417
- {
418
- awsRegion : 'us-west-2' ,
419
- instanceId : 'i-9999' ,
420
- environment : 'gi-ci' ,
421
- } ,
422
- ] ;
423
-
424
- // First region succeeds
425
- mockSSMdescribeParametersRet . mockResolvedValueOnce ( {
426
- Parameters : [ { Name : 'gi-ci-i-1234' } , { Name : 'gi-ci-i-5678' } ] ,
427
- } ) ;
428
-
429
- // Second region also gets SSM parameters but has no successful terminations to clean up
430
- mockSSMdescribeParametersRet . mockResolvedValueOnce ( {
431
- Parameters : [ ] ,
365
+ it ( 'fails to terminate' , async ( ) => {
366
+ const errMsg = 'Error message' ;
367
+ const runner : RunnerInfo = {
368
+ awsRegion : Config . Instance . awsRegion ,
369
+ instanceId : '1234' ,
370
+ } ;
371
+ mockEC2 . terminateInstances . mockClear ( ) . mockReturnValue ( {
372
+ promise : jest . fn ( ) . mockRejectedValueOnce ( Error ( errMsg ) ) ,
432
373
} ) ;
433
-
434
- // First region succeeds, second region fails
435
- mockEC2 . terminateInstances
436
- . mockReturnValueOnce ( {
437
- promise : jest . fn ( ) . mockResolvedValueOnce ( { } ) ,
438
- } )
439
- . mockReturnValueOnce ( {
440
- promise : jest . fn ( ) . mockRejectedValueOnce ( new Error ( 'Region failure' ) ) ,
441
- } ) ;
442
-
443
- await expect ( terminateRunners ( runners , metrics ) ) . rejects . toThrow (
444
- 'Failed to terminate some runners: Instance i-9999: Region failure' ,
445
- ) ;
446
-
447
- expect ( mockEC2 . terminateInstances ) . toBeCalledTimes ( 2 ) ;
448
- expect ( mockSSM . describeParameters ) . toBeCalledTimes ( 2 ) ; // Called for both regions
449
- expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 2 ) ; // Only for successful region
374
+ expect ( terminateRunner ( runner , metrics ) ) . rejects . toThrowError ( errMsg ) ;
375
+ expect ( mockSSM . describeParameters ) . not . toBeCalled ( ) ;
376
+ expect ( mockSSM . deleteParameter ) . not . toBeCalled ( ) ;
450
377
} ) ;
451
378
452
- it ( 'handles large batches by splitting into chunks' , async ( ) => {
453
- // Create 150 runners to test batching (should split into 2 batches of 100 and 50)
454
- const runners : RunnerInfo [ ] = Array . from ( { length : 150 } , ( _ , i ) => ( {
455
- awsRegion : 'us-east-1' ,
456
- instanceId : `i-${ i . toString ( ) . padStart ( 4 , '0' ) } ` ,
457
- environment : 'gi-ci' ,
458
- } ) ) ;
459
-
460
- mockSSMdescribeParametersRet . mockResolvedValueOnce ( {
461
- Parameters : runners . map ( ( runner ) => ( {
462
- Name : getParameterNameForRunner ( runner . environment as string , runner . instanceId ) ,
463
- } ) ) ,
464
- } ) ;
465
-
466
- await terminateRunners ( runners , metrics ) ;
379
+ it ( 'fails to list parameters on terminate, then force delete all next parameters' , async ( ) => {
380
+ const runner1 : RunnerInfo = {
381
+ awsRegion : Config . Instance . awsRegion ,
382
+ instanceId : '1234' ,
383
+ environment : 'environ' ,
384
+ } ;
385
+ const runner2 : RunnerInfo = {
386
+ awsRegion : Config . Instance . awsRegion ,
387
+ instanceId : '1235' ,
388
+ environment : 'environ' ,
389
+ } ;
390
+ mockSSMdescribeParametersRet . mockRejectedValueOnce ( 'Some Error' ) ;
391
+ await terminateRunner ( runner1 , metrics ) ;
392
+ await terminateRunner ( runner2 , metrics ) ;
467
393
468
- // Should make 2 terminate calls (batches of 100 and 50)
469
394
expect ( mockEC2 . terminateInstances ) . toBeCalledTimes ( 2 ) ;
470
- expect ( mockEC2 . terminateInstances ) . toHaveBeenNthCalledWith ( 1 , {
471
- InstanceIds : runners . slice ( 0 , 100 ) . map ( ( r ) => r . instanceId ) ,
472
- } ) ;
473
- expect ( mockEC2 . terminateInstances ) . toHaveBeenNthCalledWith ( 2 , {
474
- InstanceIds : runners . slice ( 100 , 150 ) . map ( ( r ) => r . instanceId ) ,
475
- } ) ;
476
-
477
- // SSM cleanup should handle all 150 parameters
478
395
expect ( mockSSM . describeParameters ) . toBeCalledTimes ( 1 ) ;
479
- expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 150 ) ;
480
- } ) ;
481
-
482
- it ( 'cleans up SSM parameters for successful batches even when later batch fails' , async ( ) => {
483
- // Create runners that will be split into 2 batches
484
- const runners : RunnerInfo [ ] = Array . from ( { length : 150 } , ( _ , i ) => ( {
485
- awsRegion : 'us-east-1' ,
486
- instanceId : `i-${ i . toString ( ) . padStart ( 4 , '0' ) } ` ,
487
- environment : 'gi-ci' ,
488
- } ) ) ;
489
-
490
- mockSSMdescribeParametersRet . mockResolvedValueOnce ( {
491
- Parameters : runners . slice ( 0 , 100 ) . map ( ( runner ) => ( {
492
- Name : getParameterNameForRunner ( runner . environment as string , runner . instanceId ) ,
493
- } ) ) ,
396
+ expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 2 ) ;
397
+ expect ( mockSSM . deleteParameter ) . toBeCalledWith ( {
398
+ Name : getParameterNameForRunner ( runner1 . environment as string , runner1 . instanceId ) ,
494
399
} ) ;
495
-
496
- // First batch succeeds, second batch fails
497
- mockEC2 . terminateInstances
498
- . mockReturnValueOnce ( {
499
- promise : jest . fn ( ) . mockResolvedValueOnce ( { } ) ,
500
- } )
501
- . mockReturnValueOnce ( {
502
- promise : jest . fn ( ) . mockRejectedValueOnce ( new Error ( 'Batch 2 failed' ) ) ,
503
- } ) ;
504
-
505
- await expect ( terminateRunners ( runners , metrics ) ) . rejects . toThrow ( 'Failed to terminate some runners' ) ;
506
-
507
- expect ( mockEC2 . terminateInstances ) . toBeCalledTimes ( 2 ) ;
508
- // SSM cleanup should still happen for the first 100 runners that were successfully terminated
509
- expect ( mockSSM . describeParameters ) . toBeCalledTimes ( 1 ) ;
510
- expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 100 ) ;
511
- } ) ;
512
-
513
- it ( 'handles SSM parameter cleanup failure gracefully' , async ( ) => {
514
- const runners : RunnerInfo [ ] = [
515
- {
516
- awsRegion : 'us-east-1' ,
517
- instanceId : 'i-1234' ,
518
- environment : 'gi-ci' ,
519
- } ,
520
- ] ;
521
-
522
- // SSM describe fails, so it should attempt direct deletion
523
- mockSSMdescribeParametersRet . mockRejectedValueOnce ( new Error ( 'SSM describe failed' ) ) ;
524
-
525
- await terminateRunners ( runners , metrics ) ;
526
-
527
- expect ( mockEC2 . terminateInstances ) . toBeCalledTimes ( 1 ) ;
528
- expect ( mockSSM . describeParameters ) . toBeCalledTimes ( 1 ) ;
529
- // Should still attempt direct deletion even when describe fails
530
- expect ( mockSSM . deleteParameter ) . toBeCalledTimes ( 1 ) ;
531
400
expect ( mockSSM . deleteParameter ) . toBeCalledWith ( {
532
- Name : getParameterNameForRunner ( runners [ 0 ] . environment as string , runners [ 0 ] . instanceId ) ,
401
+ Name : getParameterNameForRunner ( runner2 . environment as string , runner2 . instanceId ) ,
533
402
} ) ;
534
403
} ) ;
535
404
} ) ;
@@ -1756,44 +1625,3 @@ describe('createRunner', () => {
1756
1625
} ) ;
1757
1626
} ) ;
1758
1627
} ) ;
1759
-
1760
- describe ( 'terminateRunner' , ( ) => {
1761
- beforeEach ( ( ) => {
1762
- mockSSMdescribeParametersRet . mockClear ( ) ;
1763
- mockEC2 . terminateInstances . mockClear ( ) ;
1764
- mockSSM . deleteParameter . mockClear ( ) ;
1765
- const config = {
1766
- environment : 'gi-ci' ,
1767
- minimumRunningTimeInMinutes : 45 ,
1768
- } ;
1769
- jest . spyOn ( Config , 'Instance' , 'get' ) . mockImplementation ( ( ) => config as unknown as Config ) ;
1770
-
1771
- resetRunnersCaches ( ) ;
1772
- } ) ;
1773
-
1774
- it ( 'delegates to terminateRunners with single runner array' , async ( ) => {
1775
- const runner : RunnerInfo = {
1776
- awsRegion : 'us-east-1' ,
1777
- instanceId : 'i-1234' ,
1778
- environment : 'gi-ci' ,
1779
- } ;
1780
-
1781
- // Mock terminateRunners by mocking the underlying calls
1782
- mockSSMdescribeParametersRet . mockResolvedValueOnce ( {
1783
- Parameters : [ { Name : 'gi-ci-i-1234' } ] ,
1784
- } ) ;
1785
- mockEC2 . terminateInstances . mockReturnValueOnce ( {
1786
- promise : jest . fn ( ) . mockResolvedValueOnce ( { } ) ,
1787
- } ) ;
1788
-
1789
- await terminateRunner ( runner , metrics ) ;
1790
-
1791
- // Verify the calls match what terminateRunners would do with a single runner
1792
- expect ( mockEC2 . terminateInstances ) . toBeCalledWith ( {
1793
- InstanceIds : [ 'i-1234' ] ,
1794
- } ) ;
1795
- expect ( mockSSM . deleteParameter ) . toBeCalledWith ( {
1796
- Name : 'gi-ci-i-1234' ,
1797
- } ) ;
1798
- } ) ;
1799
- } ) ;
0 commit comments