Skip to content

Commit 6c3360d

Browse files
test: add tests for dynamic labels
1 parent 139bb41 commit 6c3360d

2 files changed

Lines changed: 200 additions & 0 deletions

File tree

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
@@ -392,26 +392,40 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
392392
let scaleUp = 0;
393393

394394
if (messages.length > 0 && dynamicEc2ConfigEnabled) {
395+
logger.info('Dynamic EC2 config enabled, processing labels');
396+
395397
const ec2Labels =
396398
messages[0].labels?.filter(l => l.startsWith('ghr-ec2-')) ?? [];
397399

400+
logger.info('EC2 labels detected', { ec2Labels });
401+
398402
if (ec2Labels.length > 0) {
399403
// Append all EC2 labels to runnerLabels
400404
runnerLabels = runnerLabels
401405
? `${runnerLabels},${ec2Labels.join(',')}`
402406
: ec2Labels.join(',');
403407

408+
logger.info('Updated runner labels', { runnerLabels });
409+
404410
// Extract instance type from EC2 labels
405411
const requestedInstanceType = ec2Labels
406412
.find(l => l.startsWith('ghr-ec2-instance-type:'))
407413
?.replace('ghr-ec2-instance-type:', '');
408414

409415
if (requestedInstanceType) {
410416
instanceTypes = [requestedInstanceType];
417+
logger.info('EC2 instance type requested', {
418+
instanceType: requestedInstanceType,
419+
});
420+
} else {
421+
logger.info('No EC2 instance type label found');
411422
}
423+
} else {
424+
logger.info('No EC2 labels found on message');
412425
}
413426
}
414427

428+
415429
for (const message of messages) {
416430
const messageLogger = logger.createChild({
417431
persistentKeys: {

0 commit comments

Comments
 (0)