Skip to content

Commit 91550c7

Browse files
test: add tests for dynamic labels
1 parent f298deb commit 91550c7

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
@@ -571,6 +571,192 @@ describe('scaleUp with GHES', () => {
571571
10000,
572572
);
573573
});
574+
575+
describe('Dynamic EC2 Configuration', () => {
576+
beforeEach(() => {
577+
process.env.ENABLE_ORGANIZATION_RUNNERS = 'true';
578+
process.env.ENABLE_DYNAMIC_EC2_CONFIG = 'true';
579+
process.env.ENABLE_EPHEMERAL_RUNNERS = 'true';
580+
process.env.ENABLE_JOB_QUEUED_CHECK = 'false';
581+
process.env.RUNNER_LABELS = 'base-label';
582+
process.env.INSTANCE_TYPES = 't3.medium,t3.large';
583+
process.env.RUNNER_NAME_PREFIX = 'unit-test';
584+
expectedRunnerParams = { ...EXPECTED_RUNNER_PARAMS };
585+
mockSSMClient.reset();
586+
});
587+
588+
it('appends EC2 labels to existing runner labels when EC2 labels are present', async () => {
589+
const testDataWithEc2Labels = [
590+
{
591+
...TEST_DATA_SINGLE,
592+
labels: ['ghr-ec2-instance-type:c5.2xlarge', 'ghr-ec2-custom:value'],
593+
messageId: 'test-1',
594+
},
595+
];
596+
597+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
598+
599+
// Verify createRunner was called with EC2 instance type extracted from labels
600+
expect(createRunner).toBeCalledWith(
601+
expect.objectContaining({
602+
ec2instanceCriteria: expect.objectContaining({
603+
instanceTypes: ['c5.2xlarge'],
604+
}),
605+
}),
606+
);
607+
});
608+
609+
it('extracts instance type from EC2 labels and overrides default instance types', async () => {
610+
const testDataWithEc2Labels = [
611+
{
612+
...TEST_DATA_SINGLE,
613+
labels: ['ghr-ec2-instance-type:m5.xlarge', 'ghr-ec2-disk:100'],
614+
messageId: 'test-2',
615+
},
616+
];
617+
618+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
619+
620+
expect(createRunner).toBeCalledWith(
621+
expect.objectContaining({
622+
ec2instanceCriteria: expect.objectContaining({
623+
instanceTypes: ['m5.xlarge'],
624+
}),
625+
}),
626+
);
627+
});
628+
629+
it('uses default instance types when no instance type EC2 label is provided', async () => {
630+
const testDataWithEc2Labels = [
631+
{
632+
...TEST_DATA_SINGLE,
633+
labels: ['ghr-ec2-custom:value'],
634+
messageId: 'test-3',
635+
},
636+
];
637+
638+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
639+
640+
// Should use the default INSTANCE_TYPES from environment
641+
expect(createRunner).toBeCalledWith(
642+
expect.objectContaining({
643+
ec2instanceCriteria: expect.objectContaining({
644+
instanceTypes: ['t3.medium', 't3.large'],
645+
}),
646+
}),
647+
);
648+
});
649+
650+
it('does not modify labels when EC2 labels are not present', async () => {
651+
const testDataWithoutEc2Labels = [
652+
{
653+
...TEST_DATA_SINGLE,
654+
labels: ['regular-label', 'another-label'],
655+
messageId: 'test-4',
656+
},
657+
];
658+
659+
await scaleUpModule.scaleUp(testDataWithoutEc2Labels);
660+
661+
// Should use default instance types
662+
expect(createRunner).toBeCalledWith(
663+
expect.objectContaining({
664+
ec2instanceCriteria: expect.objectContaining({
665+
instanceTypes: ['t3.medium', 't3.large'],
666+
}),
667+
}),
668+
);
669+
});
670+
671+
it('handles messages with no labels gracefully', async () => {
672+
const testDataWithNoLabels = [
673+
{
674+
...TEST_DATA_SINGLE,
675+
labels: undefined,
676+
messageId: 'test-5',
677+
},
678+
];
679+
680+
await scaleUpModule.scaleUp(testDataWithNoLabels);
681+
682+
expect(createRunner).toBeCalledWith(
683+
expect.objectContaining({
684+
ec2instanceCriteria: expect.objectContaining({
685+
instanceTypes: ['t3.medium', 't3.large'],
686+
}),
687+
}),
688+
);
689+
});
690+
691+
it('handles empty labels array', async () => {
692+
const testDataWithEmptyLabels = [
693+
{
694+
...TEST_DATA_SINGLE,
695+
labels: [],
696+
messageId: 'test-6',
697+
},
698+
];
699+
700+
await scaleUpModule.scaleUp(testDataWithEmptyLabels);
701+
702+
expect(createRunner).toBeCalledWith(
703+
expect.objectContaining({
704+
ec2instanceCriteria: expect.objectContaining({
705+
instanceTypes: ['t3.medium', 't3.large'],
706+
}),
707+
}),
708+
);
709+
});
710+
711+
it('does not process EC2 labels when ENABLE_DYNAMIC_EC2_CONFIG is disabled', async () => {
712+
process.env.ENABLE_DYNAMIC_EC2_CONFIG = 'false';
713+
714+
const testDataWithEc2Labels = [
715+
{
716+
...TEST_DATA_SINGLE,
717+
labels: ['ghr-ec2-instance-type:c5.4xlarge'],
718+
messageId: 'test-7',
719+
},
720+
];
721+
722+
await scaleUpModule.scaleUp(testDataWithEc2Labels);
723+
724+
// Should ignore EC2 labels and use default instance types
725+
expect(createRunner).toBeCalledWith(
726+
expect.objectContaining({
727+
ec2instanceCriteria: expect.objectContaining({
728+
instanceTypes: ['t3.medium', 't3.large'],
729+
}),
730+
}),
731+
);
732+
});
733+
734+
it('handles multiple EC2 labels correctly', async () => {
735+
const testDataWithMultipleEc2Labels = [
736+
{
737+
...TEST_DATA_SINGLE,
738+
labels: [
739+
'regular-label',
740+
'ghr-ec2-instance-type:r5.2xlarge',
741+
'ghr-ec2-ami:custom-ami',
742+
'ghr-ec2-disk:200',
743+
],
744+
messageId: 'test-8',
745+
},
746+
];
747+
748+
await scaleUpModule.scaleUp(testDataWithMultipleEc2Labels);
749+
750+
expect(createRunner).toBeCalledWith(
751+
expect.objectContaining({
752+
ec2instanceCriteria: expect.objectContaining({
753+
instanceTypes: ['r5.2xlarge'],
754+
}),
755+
}),
756+
);
757+
});
758+
});
759+
574760
describe('on repo level', () => {
575761
beforeEach(() => {
576762
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
@@ -416,26 +416,40 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
416416
const queuedMessages: ActionRequestMessageSQS[] = [];
417417

418418
if (messages.length > 0 && dynamicEc2ConfigEnabled) {
419+
logger.info('Dynamic EC2 config enabled, processing labels');
420+
419421
const ec2Labels =
420422
messages[0].labels?.filter(l => l.startsWith('ghr-ec2-')) ?? [];
421423

424+
logger.info('EC2 labels detected', { ec2Labels });
425+
422426
if (ec2Labels.length > 0) {
423427
// Append all EC2 labels to runnerLabels
424428
runnerLabels = runnerLabels
425429
? `${runnerLabels},${ec2Labels.join(',')}`
426430
: ec2Labels.join(',');
427431

432+
logger.info('Updated runner labels', { runnerLabels });
433+
428434
// Extract instance type from EC2 labels
429435
const requestedInstanceType = ec2Labels
430436
.find(l => l.startsWith('ghr-ec2-instance-type:'))
431437
?.replace('ghr-ec2-instance-type:', '');
432438

433439
if (requestedInstanceType) {
434440
instanceTypes = [requestedInstanceType];
441+
logger.info('EC2 instance type requested', {
442+
instanceType: requestedInstanceType,
443+
});
444+
} else {
445+
logger.info('No EC2 instance type label found');
435446
}
447+
} else {
448+
logger.info('No EC2 labels found on message');
436449
}
437450
}
438451

452+
439453
for (const message of messages) {
440454
const messageLogger = logger.createChild({
441455
persistentKeys: {

0 commit comments

Comments
 (0)