Skip to content

Commit 3a1b926

Browse files
test: add tests for dynamic labels
1 parent f7a1ddd commit 3a1b926

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,192 @@ describe('scaleUp with GHES', () => {
398398
10000,
399399
);
400400
});
401+
402+
describe('Dynamic EC2 Configuration', () => {
403+
beforeEach(() => {
404+
process.env.ENABLE_ORGANIZATION_RUNNERS = 'true';
405+
process.env.ENABLE_DYNAMIC_EC2_CONFIG = 'true';
406+
process.env.ENABLE_EPHEMERAL_RUNNERS = 'true';
407+
process.env.ENABLE_JOB_QUEUED_CHECK = 'false';
408+
process.env.RUNNER_LABELS = 'base-label';
409+
process.env.INSTANCE_TYPES = 't3.medium,t3.large';
410+
process.env.RUNNER_NAME_PREFIX = 'unit-test';
411+
expectedRunnerParams = { ...EXPECTED_RUNNER_PARAMS };
412+
mockSSMClient.reset();
413+
});
414+
415+
it('appends EC2 labels to existing runner labels when EC2 labels are present', async () => {
416+
const testDataWithEc2Labels = [
417+
{
418+
...TEST_DATA_SINGLE,
419+
labels: ['ghr-ec2-instance-type:c5.2xlarge', 'ghr-ec2-custom:value'],
420+
messageId: 'test-1',
421+
},
422+
];
423+
424+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
425+
426+
// Verify createRunner was called with EC2 instance type extracted from labels
427+
expect(createRunner).toBeCalledWith(
428+
expect.objectContaining({
429+
ec2instanceCriteria: expect.objectContaining({
430+
instanceTypes: ['c5.2xlarge'],
431+
}),
432+
}),
433+
);
434+
});
435+
436+
it('extracts instance type from EC2 labels and overrides default instance types', async () => {
437+
const testDataWithEc2Labels = [
438+
{
439+
...TEST_DATA_SINGLE,
440+
labels: ['ghr-ec2-instance-type:m5.xlarge', 'ghr-ec2-disk:100'],
441+
messageId: 'test-2',
442+
},
443+
];
444+
445+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
446+
447+
expect(createRunner).toBeCalledWith(
448+
expect.objectContaining({
449+
ec2instanceCriteria: expect.objectContaining({
450+
instanceTypes: ['m5.xlarge'],
451+
}),
452+
}),
453+
);
454+
});
455+
456+
it('uses default instance types when no instance type EC2 label is provided', async () => {
457+
const testDataWithEc2Labels = [
458+
{
459+
...TEST_DATA_SINGLE,
460+
labels: ['ghr-ec2-custom:value'],
461+
messageId: 'test-3',
462+
},
463+
];
464+
465+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
466+
467+
// Should use the default INSTANCE_TYPES from environment
468+
expect(createRunner).toBeCalledWith(
469+
expect.objectContaining({
470+
ec2instanceCriteria: expect.objectContaining({
471+
instanceTypes: ['t3.medium', 't3.large'],
472+
}),
473+
}),
474+
);
475+
});
476+
477+
it('does not modify labels when EC2 labels are not present', async () => {
478+
const testDataWithoutEc2Labels = [
479+
{
480+
...TEST_DATA_SINGLE,
481+
labels: ['regular-label', 'another-label'],
482+
messageId: 'test-4',
483+
},
484+
];
485+
486+
await scaleUpModule.scaleUp(testDataWithoutEc2Labels);
487+
488+
// Should use default instance types
489+
expect(createRunner).toBeCalledWith(
490+
expect.objectContaining({
491+
ec2instanceCriteria: expect.objectContaining({
492+
instanceTypes: ['t3.medium', 't3.large'],
493+
}),
494+
}),
495+
);
496+
});
497+
498+
it('handles messages with no labels gracefully', async () => {
499+
const testDataWithNoLabels = [
500+
{
501+
...TEST_DATA_SINGLE,
502+
labels: undefined,
503+
messageId: 'test-5',
504+
},
505+
];
506+
507+
await scaleUpModule.scaleUp(testDataWithNoLabels);
508+
509+
expect(createRunner).toBeCalledWith(
510+
expect.objectContaining({
511+
ec2instanceCriteria: expect.objectContaining({
512+
instanceTypes: ['t3.medium', 't3.large'],
513+
}),
514+
}),
515+
);
516+
});
517+
518+
it('handles empty labels array', async () => {
519+
const testDataWithEmptyLabels = [
520+
{
521+
...TEST_DATA_SINGLE,
522+
labels: [],
523+
messageId: 'test-6',
524+
},
525+
];
526+
527+
await scaleUpModule.scaleUp(testDataWithEmptyLabels);
528+
529+
expect(createRunner).toBeCalledWith(
530+
expect.objectContaining({
531+
ec2instanceCriteria: expect.objectContaining({
532+
instanceTypes: ['t3.medium', 't3.large'],
533+
}),
534+
}),
535+
);
536+
});
537+
538+
it('does not process EC2 labels when ENABLE_DYNAMIC_EC2_CONFIG is disabled', async () => {
539+
process.env.ENABLE_DYNAMIC_EC2_CONFIG = 'false';
540+
541+
const testDataWithEc2Labels = [
542+
{
543+
...TEST_DATA_SINGLE,
544+
labels: ['ghr-ec2-instance-type:c5.4xlarge'],
545+
messageId: 'test-7',
546+
},
547+
];
548+
549+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
550+
551+
// Should ignore EC2 labels and use default instance types
552+
expect(createRunner).toBeCalledWith(
553+
expect.objectContaining({
554+
ec2instanceCriteria: expect.objectContaining({
555+
instanceTypes: ['t3.medium', 't3.large'],
556+
}),
557+
}),
558+
);
559+
});
560+
561+
it('handles multiple EC2 labels correctly', async () => {
562+
const testDataWithMultipleEc2Labels = [
563+
{
564+
...TEST_DATA_SINGLE,
565+
labels: [
566+
'regular-label',
567+
'ghr-ec2-instance-type:r5.2xlarge',
568+
'ghr-ec2-ami:custom-ami',
569+
'ghr-ec2-disk:200',
570+
],
571+
messageId: 'test-8',
572+
},
573+
];
574+
575+
await scaleUpModule.scaleUp(testDataWithMultipleEc2Labels);
576+
577+
expect(createRunner).toBeCalledWith(
578+
expect.objectContaining({
579+
ec2instanceCriteria: expect.objectContaining({
580+
instanceTypes: ['r5.2xlarge'],
581+
}),
582+
}),
583+
);
584+
});
585+
});
586+
401587
describe('on repo level', () => {
402588
beforeEach(() => {
403589
process.env.ENABLE_ORGANIZATION_RUNNERS = 'false';

lambdas/functions/control-plane/src/scale-runners/scale-up.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,26 +353,40 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
353353
let scaleUp = 0;
354354

355355
if (messages.length > 0 && dynamicEc2ConfigEnabled) {
356+
logger.info('Dynamic EC2 config enabled, processing labels');
357+
356358
const ec2Labels =
357359
messages[0].labels?.filter(l => l.startsWith('ghr-ec2-')) ?? [];
358360

361+
logger.info('EC2 labels detected', { ec2Labels });
362+
359363
if (ec2Labels.length > 0) {
360364
// Append all EC2 labels to runnerLabels
361365
runnerLabels = runnerLabels
362366
? `${runnerLabels},${ec2Labels.join(',')}`
363367
: ec2Labels.join(',');
364368

369+
logger.info('Updated runner labels', { runnerLabels });
370+
365371
// Extract instance type from EC2 labels
366372
const requestedInstanceType = ec2Labels
367373
.find(l => l.startsWith('ghr-ec2-instance-type:'))
368374
?.replace('ghr-ec2-instance-type:', '');
369375

370376
if (requestedInstanceType) {
371377
instanceTypes = [requestedInstanceType];
378+
logger.info('EC2 instance type requested', {
379+
instanceType: requestedInstanceType,
380+
});
381+
} else {
382+
logger.info('No EC2 instance type label found');
372383
}
384+
} else {
385+
logger.info('No EC2 labels found on message');
373386
}
374387
}
375388

389+
376390
for (const message of messages) {
377391
const messageLogger = logger.createChild({
378392
persistentKeys: {

0 commit comments

Comments
 (0)