From e4e02501a17c05e65eaad86da82cb5613a8176c2 Mon Sep 17 00:00:00 2001 From: Piotr Jakubowski Date: Tue, 24 Mar 2026 16:13:59 +0100 Subject: [PATCH 1/5] feat(runners): support OnDemandOptions.AllocationStrategy in EC2 Fleet Previously, createInstances always set SpotOptions.AllocationStrategy even for on-demand fleets, making instance_allocation_strategy and instance_types ordering meaningless for on-demand users wanting `prioritized`. Now the fleet request conditionally sets SpotOptions or OnDemandOptions based on targetCapacityType, and the spot-to- on-demand failover path maps spot-only strategies to `lowest-price`. --- .../control-plane/src/aws/runners.d.ts | 8 ++- .../control-plane/src/aws/runners.test.ts | 49 +++++++++++++++---- .../control-plane/src/aws/runners.ts | 32 ++++++++++-- modules/multi-runner/variables.tf | 2 +- modules/runners/variables.tf | 4 +- variables.tf | 4 +- 6 files changed, 78 insertions(+), 21 deletions(-) diff --git a/lambdas/functions/control-plane/src/aws/runners.d.ts b/lambdas/functions/control-plane/src/aws/runners.d.ts index c891500f27..29ac993b88 100644 --- a/lambdas/functions/control-plane/src/aws/runners.d.ts +++ b/lambdas/functions/control-plane/src/aws/runners.d.ts @@ -1,4 +1,8 @@ -import { DefaultTargetCapacityType, SpotAllocationStrategy } from '@aws-sdk/client-ec2'; +import { + DefaultTargetCapacityType, + FleetOnDemandAllocationStrategy, + SpotAllocationStrategy, +} from '@aws-sdk/client-ec2'; import { LambdaRunnerSource } from '../scale-runners/scale-up'; export type RunnerType = 'Org' | 'Repo'; @@ -40,7 +44,7 @@ export interface RunnerInputParameters { instanceTypes: string[]; targetCapacityType: DefaultTargetCapacityType; maxSpotPrice?: string; - instanceAllocationStrategy: SpotAllocationStrategy; + instanceAllocationStrategy: SpotAllocationStrategy | FleetOnDemandAllocationStrategy; }; numberOfRunners: number; source: LambdaRunnerSource; diff --git a/lambdas/functions/control-plane/src/aws/runners.test.ts b/lambdas/functions/control-plane/src/aws/runners.test.ts index 4243e4b06b..ca5c6296d6 100644 --- a/lambdas/functions/control-plane/src/aws/runners.test.ts +++ b/lambdas/functions/control-plane/src/aws/runners.test.ts @@ -10,6 +10,7 @@ import { DescribeInstancesCommand, type DescribeInstancesResult, EC2Client, + FleetOnDemandAllocationStrategy, SpotAllocationStrategy, TerminateInstancesCommand, } from '@aws-sdk/client-ec2'; @@ -389,11 +390,31 @@ describe('create runner', () => { }); it('calls create fleet of 1 instance with the on-demand capacity', async () => { - await createRunner(createRunnerConfig({ ...defaultRunnerConfig, capacityType: 'on-demand' })); + await createRunner( + createRunnerConfig({ ...defaultRunnerConfig, capacityType: 'on-demand', allocationStrategy: 'lowest-price' }), + ); expect(mockEC2Client).toHaveReceivedCommandWith(CreateFleetCommand, { ...expectedCreateFleetRequest({ ...defaultExpectedFleetRequestValues, capacityType: 'on-demand', + allocationStrategy: 'lowest-price', + }), + }); + }); + + it('calls create fleet with on-demand capacity and prioritized allocation strategy', async () => { + await createRunner( + createRunnerConfig({ + ...defaultRunnerConfig, + capacityType: 'on-demand', + allocationStrategy: FleetOnDemandAllocationStrategy.PRIORITIZED, + }), + ); + expect(mockEC2Client).toHaveReceivedCommandWith(CreateFleetCommand, { + ...expectedCreateFleetRequest({ + ...defaultExpectedFleetRequestValues, + capacityType: 'on-demand', + allocationStrategy: FleetOnDemandAllocationStrategy.PRIORITIZED, }), }); }); @@ -631,12 +652,13 @@ describe('create runner with errors fail over to OnDemand', () => { }), }); - // second call with with OnDemand fallback + // second call with with OnDemand fallback, allocation strategy defaults to lowest-price expect(mockEC2Client).toHaveReceivedNthCommandWith(2, CreateFleetCommand, { ...expectedCreateFleetRequest({ ...defaultExpectedFleetRequestValues, totalTargetCapacity: 1, capacityType: 'on-demand', + allocationStrategy: 'lowest-price', }), }); }); @@ -673,12 +695,13 @@ describe('create runner with errors fail over to OnDemand', () => { }), }); - // second call with with OnDemand failback, capacity is reduced by 1 + // second call with with OnDemand failback, capacity is reduced by 1, allocation strategy defaults to lowest-price expect(mockEC2Client).toHaveReceivedNthCommandWith(2, CreateFleetCommand, { ...expectedCreateFleetRequest({ ...defaultExpectedFleetRequestValues, totalTargetCapacity: 1, capacityType: 'on-demand', + allocationStrategy: 'lowest-price', }), }); }); @@ -748,7 +771,7 @@ function createFleetMockWithWithOnDemandFallback(errors: string[], instances?: s interface RunnerConfig { type: RunnerType; capacityType: DefaultTargetCapacityType; - allocationStrategy: SpotAllocationStrategy; + allocationStrategy: SpotAllocationStrategy | FleetOnDemandAllocationStrategy; maxSpotPrice?: string; amiIdSsmParameterName?: string; tracingEnabled?: boolean; @@ -782,7 +805,7 @@ function createRunnerConfig(runnerConfig: RunnerConfig): RunnerInputParameters { interface ExpectedFleetRequestValues { type: 'Repo' | 'Org'; capacityType: DefaultTargetCapacityType; - allocationStrategy: SpotAllocationStrategy; + allocationStrategy: SpotAllocationStrategy | FleetOnDemandAllocationStrategy; maxSpotPrice?: string; totalTargetCapacity: number; imageId?: string; @@ -831,10 +854,18 @@ function expectedCreateFleetRequest(expectedValues: ExpectedFleetRequestValues): ], }, ], - SpotOptions: { - AllocationStrategy: expectedValues.allocationStrategy, - MaxTotalPrice: expectedValues.maxSpotPrice, - }, + ...(expectedValues.capacityType === 'spot' + ? { + SpotOptions: { + AllocationStrategy: expectedValues.allocationStrategy, + MaxTotalPrice: expectedValues.maxSpotPrice, + }, + } + : { + OnDemandOptions: { + AllocationStrategy: expectedValues.allocationStrategy, + }, + }), TagSpecifications: [ { ResourceType: 'instance', diff --git a/lambdas/functions/control-plane/src/aws/runners.ts b/lambdas/functions/control-plane/src/aws/runners.ts index 193c82d2e7..cd10755007 100644 --- a/lambdas/functions/control-plane/src/aws/runners.ts +++ b/lambdas/functions/control-plane/src/aws/runners.ts @@ -7,6 +7,8 @@ import { DescribeInstancesResult, EC2Client, FleetLaunchTemplateOverridesRequest, + FleetOnDemandAllocationStrategy, + SpotAllocationStrategy, Tag, TerminateInstancesCommand, _InstanceType, @@ -187,11 +189,21 @@ async function processFleetResult( logger.warn(`Create fleet failed, initatiing fall back to on demand instances.`); logger.debug('Create fleet failed.', { data: fleet.Errors }); const numberOfInstances = runnerParameters.numberOfRunners - instances.length; + const onDemandValidStrategies = ['lowest-price', 'prioritized']; + const failoverAllocationStrategy = onDemandValidStrategies.includes( + runnerParameters.ec2instanceCriteria.instanceAllocationStrategy, + ) + ? runnerParameters.ec2instanceCriteria.instanceAllocationStrategy + : 'lowest-price'; const instancesOnDemand = await createRunner({ ...runnerParameters, numberOfRunners: numberOfInstances, onDemandFailoverOnError: ['InsufficientInstanceCapacity'], - ec2instanceCriteria: { ...runnerParameters.ec2instanceCriteria, targetCapacityType: 'on-demand' }, + ec2instanceCriteria: { + ...runnerParameters.ec2instanceCriteria, + targetCapacityType: 'on-demand', + instanceAllocationStrategy: failoverAllocationStrategy, + }, }); instances.push(...instancesOnDemand); return instances; @@ -268,10 +280,20 @@ async function createInstances( ), }, ], - SpotOptions: { - MaxTotalPrice: runnerParameters.ec2instanceCriteria.maxSpotPrice, - AllocationStrategy: runnerParameters.ec2instanceCriteria.instanceAllocationStrategy, - }, + ...(runnerParameters.ec2instanceCriteria.targetCapacityType === 'spot' + ? { + SpotOptions: { + MaxTotalPrice: runnerParameters.ec2instanceCriteria.maxSpotPrice, + AllocationStrategy: + runnerParameters.ec2instanceCriteria.instanceAllocationStrategy as SpotAllocationStrategy, + }, + } + : { + OnDemandOptions: { + AllocationStrategy: + runnerParameters.ec2instanceCriteria.instanceAllocationStrategy as FleetOnDemandAllocationStrategy, + }, + }), TargetCapacitySpecification: { TotalTargetCapacity: runnerParameters.numberOfRunners, DefaultTargetCapacityType: runnerParameters.ec2instanceCriteria.targetCapacityType, diff --git a/modules/multi-runner/variables.tf b/modules/multi-runner/variables.tf index 613cf8b2ce..a48abd7712 100644 --- a/modules/multi-runner/variables.tf +++ b/modules/multi-runner/variables.tf @@ -214,7 +214,7 @@ variable "multi_runner_config" { enable_runner_binaries_syncer: "Option to disable the lambda to sync GitHub runner distribution, useful when using a pre-build AMI." enable_ssm_on_runners: "Enable to allow access the runner instances for debugging purposes via SSM. Note that this adds additional permissions to the runner instances." enable_userdata: "Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI." - instance_allocation_strategy: "The allocation strategy for spot instances. AWS recommends to use `capacity-optimized` however the AWS default is `lowest-price`." + instance_allocation_strategy: "The allocation strategy for creating instances. For spot, AWS recommends `capacity-optimized`; for on-demand, use `lowest-price` or `prioritized`. The AWS default is `lowest-price`." instance_max_spot_price: "Max price price for spot instances per hour. This variable will be passed to the create fleet as max spot price for the fleet." instance_target_capacity_type: "Default lifecycle used for runner instances, can be either `spot` or `on-demand`." instance_types: "List of instance types for the action runner. Defaults are based on runner_os (al2023 for linux and Windows Server Core for win)." diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index e2a33280b9..762f16c007 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -102,12 +102,12 @@ variable "instance_target_capacity_type" { } variable "instance_allocation_strategy" { - description = "The allocation strategy for spot instances. AWS recommends to use `capacity-optimized` however the AWS default is `lowest-price`." + description = "The allocation strategy for creating instances. For spot, AWS recommends `capacity-optimized`; for on-demand, use `lowest-price` or `prioritized`. The AWS default is `lowest-price`." type = string default = "lowest-price" validation { - condition = contains(["lowest-price", "diversified", "capacity-optimized", "capacity-optimized-prioritized", "price-capacity-optimized"], var.instance_allocation_strategy) + condition = contains(["lowest-price", "diversified", "capacity-optimized", "capacity-optimized-prioritized", "price-capacity-optimized", "prioritized"], var.instance_allocation_strategy) error_message = "The instance allocation strategy does not match the allowed values." } } diff --git a/variables.tf b/variables.tf index d739e916fb..9abfedfc9d 100644 --- a/variables.tf +++ b/variables.tf @@ -554,11 +554,11 @@ variable "instance_target_capacity_type" { } variable "instance_allocation_strategy" { - description = "The allocation strategy for spot instances. AWS recommends using `price-capacity-optimized` however the AWS default is `lowest-price`." + description = "The allocation strategy for creating instances. For spot, AWS recommends `price-capacity-optimized`; for on-demand, use `lowest-price` or `prioritized`. The AWS default is `lowest-price`." type = string default = "lowest-price" validation { - condition = contains(["lowest-price", "diversified", "capacity-optimized", "capacity-optimized-prioritized", "price-capacity-optimized"], var.instance_allocation_strategy) + condition = contains(["lowest-price", "diversified", "capacity-optimized", "capacity-optimized-prioritized", "price-capacity-optimized", "prioritized"], var.instance_allocation_strategy) error_message = "The instance allocation strategy does not match the allowed values." } } From 697306543db3f89f028c1448e0eb17c39c50bd01 Mon Sep 17 00:00:00 2001 From: Piotr Jakubowski Date: Tue, 24 Mar 2026 16:33:00 +0100 Subject: [PATCH 2/5] feat(runners): add instance_type_priorities for prioritized allocation Add optional `instance_type_priorities` variable (map of instance type to priority number) to control EC2 Fleet override priorities. When not set, priorities default to the index position in `instance_types`, preserving the user's ordering. This makes the `prioritized` allocation strategy work correctly for both spot and on-demand fleets. --- .../control-plane/src/aws/runners.d.ts | 1 + .../control-plane/src/aws/runners.test.ts | 27 +++++++++++++++++++ .../control-plane/src/aws/runners.ts | 5 +++- .../functions/control-plane/src/pool/pool.ts | 4 +++ .../src/scale-runners/scale-up.ts | 4 +++ main.tf | 1 + modules/multi-runner/runners.tf | 1 + modules/multi-runner/variables.tf | 2 ++ modules/runners/pool.tf | 1 + modules/runners/pool/main.tf | 1 + modules/runners/pool/variables.tf | 1 + modules/runners/scale-up.tf | 1 + modules/runners/variables.tf | 6 +++++ variables.tf | 6 +++++ 14 files changed, 60 insertions(+), 1 deletion(-) diff --git a/lambdas/functions/control-plane/src/aws/runners.d.ts b/lambdas/functions/control-plane/src/aws/runners.d.ts index 29ac993b88..d199391aab 100644 --- a/lambdas/functions/control-plane/src/aws/runners.d.ts +++ b/lambdas/functions/control-plane/src/aws/runners.d.ts @@ -42,6 +42,7 @@ export interface RunnerInputParameters { launchTemplateName: string; ec2instanceCriteria: { instanceTypes: string[]; + instanceTypePriorities?: Record; targetCapacityType: DefaultTargetCapacityType; maxSpotPrice?: string; instanceAllocationStrategy: SpotAllocationStrategy | FleetOnDemandAllocationStrategy; diff --git a/lambdas/functions/control-plane/src/aws/runners.test.ts b/lambdas/functions/control-plane/src/aws/runners.test.ts index ca5c6296d6..d8bf92a41b 100644 --- a/lambdas/functions/control-plane/src/aws/runners.test.ts +++ b/lambdas/functions/control-plane/src/aws/runners.test.ts @@ -419,6 +419,26 @@ describe('create runner', () => { }); }); + it('calls create fleet with custom instance type priorities', async () => { + const priorities = { 'm5.large': 10, 'c5.large': 5 }; + await createRunner( + createRunnerConfig({ + ...defaultRunnerConfig, + capacityType: 'on-demand', + allocationStrategy: FleetOnDemandAllocationStrategy.PRIORITIZED, + instanceTypePriorities: priorities, + }), + ); + expect(mockEC2Client).toHaveReceivedCommandWith(CreateFleetCommand, { + ...expectedCreateFleetRequest({ + ...defaultExpectedFleetRequestValues, + capacityType: 'on-demand', + allocationStrategy: FleetOnDemandAllocationStrategy.PRIORITIZED, + instanceTypePriorities: priorities, + }), + }); + }); + it('calls run instances with the on-demand capacity', async () => { await createRunner(createRunnerConfig({ ...defaultRunnerConfig, maxSpotPrice: '0.1' })); expect(mockEC2Client).toHaveReceivedCommandWith(CreateFleetCommand, { @@ -772,6 +792,7 @@ interface RunnerConfig { type: RunnerType; capacityType: DefaultTargetCapacityType; allocationStrategy: SpotAllocationStrategy | FleetOnDemandAllocationStrategy; + instanceTypePriorities?: Record; maxSpotPrice?: string; amiIdSsmParameterName?: string; tracingEnabled?: boolean; @@ -789,6 +810,7 @@ function createRunnerConfig(runnerConfig: RunnerConfig): RunnerInputParameters { launchTemplateName: LAUNCH_TEMPLATE, ec2instanceCriteria: { instanceTypes: ['m5.large', 'c5.large'], + instanceTypePriorities: runnerConfig.instanceTypePriorities, targetCapacityType: runnerConfig.capacityType, maxSpotPrice: runnerConfig.maxSpotPrice, instanceAllocationStrategy: runnerConfig.allocationStrategy, @@ -806,6 +828,7 @@ interface ExpectedFleetRequestValues { type: 'Repo' | 'Org'; capacityType: DefaultTargetCapacityType; allocationStrategy: SpotAllocationStrategy | FleetOnDemandAllocationStrategy; + instanceTypePriorities?: Record; maxSpotPrice?: string; totalTargetCapacity: number; imageId?: string; @@ -838,18 +861,22 @@ function expectedCreateFleetRequest(expectedValues: ExpectedFleetRequestValues): { InstanceType: 'm5.large', SubnetId: 'subnet-123', + Priority: expectedValues.instanceTypePriorities?.['m5.large'] ?? 0, }, { InstanceType: 'c5.large', SubnetId: 'subnet-123', + Priority: expectedValues.instanceTypePriorities?.['c5.large'] ?? 1, }, { InstanceType: 'm5.large', SubnetId: 'subnet-456', + Priority: expectedValues.instanceTypePriorities?.['m5.large'] ?? 0, }, { InstanceType: 'c5.large', SubnetId: 'subnet-456', + Priority: expectedValues.instanceTypePriorities?.['c5.large'] ?? 1, }, ], }, diff --git a/lambdas/functions/control-plane/src/aws/runners.ts b/lambdas/functions/control-plane/src/aws/runners.ts index cd10755007..e8329283ff 100644 --- a/lambdas/functions/control-plane/src/aws/runners.ts +++ b/lambdas/functions/control-plane/src/aws/runners.ts @@ -127,14 +127,16 @@ function generateFleetOverrides( subnetIds: string[], instancesTypes: string[], amiId?: string, + instanceTypePriorities?: Record, ): FleetLaunchTemplateOverridesRequest[] { const result: FleetLaunchTemplateOverridesRequest[] = []; subnetIds.forEach((s) => { - instancesTypes.forEach((i) => { + instancesTypes.forEach((i, index) => { const item: FleetLaunchTemplateOverridesRequest = { SubnetId: s, InstanceType: i as _InstanceType, ImageId: amiId, + Priority: instanceTypePriorities?.[i] ?? index, }; result.push(item); }); @@ -277,6 +279,7 @@ async function createInstances( runnerParameters.subnets, runnerParameters.ec2instanceCriteria.instanceTypes, amiIdOverride, + runnerParameters.ec2instanceCriteria.instanceTypePriorities, ), }, ], diff --git a/lambdas/functions/control-plane/src/pool/pool.ts b/lambdas/functions/control-plane/src/pool/pool.ts index cece8d9951..c5cfcd1b7e 100644 --- a/lambdas/functions/control-plane/src/pool/pool.ts +++ b/lambdas/functions/control-plane/src/pool/pool.ts @@ -36,6 +36,9 @@ export async function adjust(event: PoolEvent): Promise { const launchTemplateName = process.env.LAUNCH_TEMPLATE_NAME; const instanceMaxSpotPrice = process.env.INSTANCE_MAX_SPOT_PRICE; const instanceAllocationStrategy = process.env.INSTANCE_ALLOCATION_STRATEGY || 'lowest-price'; // same as AWS default + const instanceTypePriorities = process.env.INSTANCE_TYPE_PRIORITIES + ? (JSON.parse(process.env.INSTANCE_TYPE_PRIORITIES) as Record) + : undefined; const runnerOwner = process.env.RUNNER_OWNER; const amiIdSsmParameterName = process.env.AMI_ID_SSM_PARAMETER_NAME; const tracingEnabled = yn(process.env.POWERTOOLS_TRACE_ENABLED, { default: false }); @@ -92,6 +95,7 @@ export async function adjust(event: PoolEvent): Promise { { ec2instanceCriteria: { instanceTypes, + instanceTypePriorities, targetCapacityType: instanceTargetCapacityType, maxSpotPrice: instanceMaxSpotPrice, instanceAllocationStrategy: instanceAllocationStrategy, diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts index 395c87e8f8..2f99260563 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.ts @@ -308,6 +308,9 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise) + : undefined; const enableJobQueuedCheck = yn(process.env.ENABLE_JOB_QUEUED_CHECK, { default: true }); const amiIdSsmParameterName = process.env.AMI_ID_SSM_PARAMETER_NAME; const runnerNamePrefix = process.env.RUNNER_NAME_PREFIX || ''; @@ -497,6 +500,7 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise Date: Tue, 24 Mar 2026 16:41:16 +0100 Subject: [PATCH 3/5] style(runners): fix prettier formatting --- lambdas/functions/control-plane/src/aws/runners.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lambdas/functions/control-plane/src/aws/runners.ts b/lambdas/functions/control-plane/src/aws/runners.ts index e8329283ff..c449dda116 100644 --- a/lambdas/functions/control-plane/src/aws/runners.ts +++ b/lambdas/functions/control-plane/src/aws/runners.ts @@ -287,14 +287,14 @@ async function createInstances( ? { SpotOptions: { MaxTotalPrice: runnerParameters.ec2instanceCriteria.maxSpotPrice, - AllocationStrategy: - runnerParameters.ec2instanceCriteria.instanceAllocationStrategy as SpotAllocationStrategy, + AllocationStrategy: runnerParameters.ec2instanceCriteria + .instanceAllocationStrategy as SpotAllocationStrategy, }, } : { OnDemandOptions: { - AllocationStrategy: - runnerParameters.ec2instanceCriteria.instanceAllocationStrategy as FleetOnDemandAllocationStrategy, + AllocationStrategy: runnerParameters.ec2instanceCriteria + .instanceAllocationStrategy as FleetOnDemandAllocationStrategy, }, }), TargetCapacitySpecification: { From 68c399a96caccee0aa1c107bc5e11849c4eeb198 Mon Sep 17 00:00:00 2001 From: Piotr Jakubowski Date: Tue, 24 Mar 2026 17:00:14 +0100 Subject: [PATCH 4/5] refactor(runners): only set Priority on overrides for prioritized strategy --- .../control-plane/src/aws/runners.test.ts | 16 ++++++++++++---- .../functions/control-plane/src/aws/runners.ts | 6 ++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lambdas/functions/control-plane/src/aws/runners.test.ts b/lambdas/functions/control-plane/src/aws/runners.test.ts index d8bf92a41b..10cc75d116 100644 --- a/lambdas/functions/control-plane/src/aws/runners.test.ts +++ b/lambdas/functions/control-plane/src/aws/runners.test.ts @@ -861,22 +861,30 @@ function expectedCreateFleetRequest(expectedValues: ExpectedFleetRequestValues): { InstanceType: 'm5.large', SubnetId: 'subnet-123', - Priority: expectedValues.instanceTypePriorities?.['m5.large'] ?? 0, + ...(expectedValues.allocationStrategy === 'prioritized' && { + Priority: expectedValues.instanceTypePriorities?.['m5.large'] ?? 0, + }), }, { InstanceType: 'c5.large', SubnetId: 'subnet-123', - Priority: expectedValues.instanceTypePriorities?.['c5.large'] ?? 1, + ...(expectedValues.allocationStrategy === 'prioritized' && { + Priority: expectedValues.instanceTypePriorities?.['c5.large'] ?? 1, + }), }, { InstanceType: 'm5.large', SubnetId: 'subnet-456', - Priority: expectedValues.instanceTypePriorities?.['m5.large'] ?? 0, + ...(expectedValues.allocationStrategy === 'prioritized' && { + Priority: expectedValues.instanceTypePriorities?.['m5.large'] ?? 0, + }), }, { InstanceType: 'c5.large', SubnetId: 'subnet-456', - Priority: expectedValues.instanceTypePriorities?.['c5.large'] ?? 1, + ...(expectedValues.allocationStrategy === 'prioritized' && { + Priority: expectedValues.instanceTypePriorities?.['c5.large'] ?? 1, + }), }, ], }, diff --git a/lambdas/functions/control-plane/src/aws/runners.ts b/lambdas/functions/control-plane/src/aws/runners.ts index c449dda116..79ddff2547 100644 --- a/lambdas/functions/control-plane/src/aws/runners.ts +++ b/lambdas/functions/control-plane/src/aws/runners.ts @@ -136,7 +136,7 @@ function generateFleetOverrides( SubnetId: s, InstanceType: i as _InstanceType, ImageId: amiId, - Priority: instanceTypePriorities?.[i] ?? index, + ...(instanceTypePriorities !== undefined && { Priority: instanceTypePriorities[i] ?? index }), }; result.push(item); }); @@ -279,7 +279,9 @@ async function createInstances( runnerParameters.subnets, runnerParameters.ec2instanceCriteria.instanceTypes, amiIdOverride, - runnerParameters.ec2instanceCriteria.instanceTypePriorities, + runnerParameters.ec2instanceCriteria.instanceAllocationStrategy === 'prioritized' + ? (runnerParameters.ec2instanceCriteria.instanceTypePriorities ?? {}) + : undefined, ), }, ], From 17fc96046eac07ee618993c4556b85cc6c238055 Mon Sep 17 00:00:00 2001 From: Piotr Jakubowski Date: Tue, 24 Mar 2026 17:11:20 +0100 Subject: [PATCH 5/5] refactor(runners): pass allocationStrategy to generateFleetOverrides Move the prioritized strategy check into generateFleetOverrides itself rather than having the caller decide what to pass, making the logic more explicit and self-contained. --- lambdas/functions/control-plane/src/aws/runners.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lambdas/functions/control-plane/src/aws/runners.ts b/lambdas/functions/control-plane/src/aws/runners.ts index 79ddff2547..ba1d85bba4 100644 --- a/lambdas/functions/control-plane/src/aws/runners.ts +++ b/lambdas/functions/control-plane/src/aws/runners.ts @@ -127,6 +127,7 @@ function generateFleetOverrides( subnetIds: string[], instancesTypes: string[], amiId?: string, + allocationStrategy?: string, instanceTypePriorities?: Record, ): FleetLaunchTemplateOverridesRequest[] { const result: FleetLaunchTemplateOverridesRequest[] = []; @@ -136,7 +137,7 @@ function generateFleetOverrides( SubnetId: s, InstanceType: i as _InstanceType, ImageId: amiId, - ...(instanceTypePriorities !== undefined && { Priority: instanceTypePriorities[i] ?? index }), + ...(allocationStrategy === 'prioritized' && { Priority: instanceTypePriorities?.[i] ?? index }), }; result.push(item); }); @@ -279,9 +280,8 @@ async function createInstances( runnerParameters.subnets, runnerParameters.ec2instanceCriteria.instanceTypes, amiIdOverride, - runnerParameters.ec2instanceCriteria.instanceAllocationStrategy === 'prioritized' - ? (runnerParameters.ec2instanceCriteria.instanceTypePriorities ?? {}) - : undefined, + runnerParameters.ec2instanceCriteria.instanceAllocationStrategy, + runnerParameters.ec2instanceCriteria.instanceTypePriorities, ), }, ],