diff --git a/modules/runners/lambdas/runners/src/scale-runners/runners.ts b/modules/runners/lambdas/runners/src/scale-runners/runners.ts index d2a5c4b92d..4f0e6a35b9 100644 --- a/modules/runners/lambdas/runners/src/scale-runners/runners.ts +++ b/modules/runners/lambdas/runners/src/scale-runners/runners.ts @@ -13,6 +13,26 @@ export interface ListRunnerFilters { environment?: string; } +export interface instanceParams { + MaxCount: number, + MinCount: number, + LaunchTemplate: { + LaunchTemplateName: string, + Version: string, + }, + SubnetId: string, + TagSpecifications: [ + { + ResourceType: string, + Tags: [ + { Key: string, Value?: string }, + { Key: string, Value?: string }, + ], + }, + ], + InstanceType?: string +} + export async function listRunners(filters: ListRunnerFilters | undefined = undefined): Promise { const ec2 = new EC2(); const ec2Filters = [ @@ -73,40 +93,57 @@ export async function createRunner(runnerParameters: RunnerInputParameters): Pro const subnets = (process.env.SUBNET_IDS as string).split(','); const randomSubnet = subnets[Math.floor(Math.random() * subnets.length)]; console.debug('Runner configuration: ' + JSON.stringify(runnerParameters)); - const ec2 = new EC2(); - const runInstancesResponse = await ec2 - .runInstances({ - MaxCount: 1, - MinCount: 1, - LaunchTemplate: { - LaunchTemplateName: launchTemplateName, - Version: launchTemplateVersion, + + const instanceParams : instanceParams = { + MaxCount: 1, + MinCount: 1, + LaunchTemplate: { + LaunchTemplateName: launchTemplateName, + Version: launchTemplateVersion, + }, + SubnetId: randomSubnet, + TagSpecifications: [ + { + ResourceType: 'instance', + Tags: [ + { Key: 'Application', Value: 'github-action-runner' }, + { + Key: runnerParameters.orgName ? 'Org' : 'Repo', + Value: runnerParameters.orgName ? runnerParameters.orgName : runnerParameters.repoName, + }, + ], }, - SubnetId: randomSubnet, - TagSpecifications: [ - { - ResourceType: 'instance', - Tags: [ - { Key: 'Application', Value: 'github-action-runner' }, - { - Key: runnerParameters.orgName ? 'Org' : 'Repo', - Value: runnerParameters.orgName ? runnerParameters.orgName : runnerParameters.repoName, - }, - ], - }, - ], - }) - .promise(); - console.info('Created instance(s): ', runInstancesResponse.Instances?.map((i) => i.InstanceId).join(',')); + ], + } - const ssm = new SSM(); - runInstancesResponse.Instances?.forEach(async (i: EC2.Instance) => { - await ssm - .putParameter({ - Name: runnerParameters.environment + '-' + (i.InstanceId as string), - Value: runnerParameters.runnerConfig, - Type: 'SecureString', + const ec2 = new EC2(); + try { + const runInstancesResponse = await ec2 + .runInstances(instanceParams, + async function(err, data) { + if (err) { + console.info(err) + instanceParams.InstanceType = process.env.SECONDARY_INSTANCE_TYPE as string; + return await ec2.runInstances(instanceParams) + } + return data; }) .promise(); - }); + + console.info('Created instance(s): ', runInstancesResponse.Instances?.map((i) => i.InstanceId).join(',')); + const ssm = new SSM(); + runInstancesResponse.Instances?.forEach(async (i: EC2.Instance) => { + await ssm + .putParameter({ + Name: runnerParameters.environment + '-' + (i.InstanceId as string), + Value: runnerParameters.runnerConfig, + Type: 'SecureString', + }) + .promise(); + }); + } + catch (e) { + console.error(e); + } + } diff --git a/modules/runners/scale-up.tf b/modules/runners/scale-up.tf index 5418880662..611789d513 100644 --- a/modules/runners/scale-up.tf +++ b/modules/runners/scale-up.tf @@ -42,6 +42,7 @@ resource "aws_lambda_function" "scale_up" { LAUNCH_TEMPLATE_NAME = aws_launch_template.runner.name LAUNCH_TEMPLATE_VERSION = aws_launch_template.runner.latest_version SUBNET_IDS = join(",", var.subnet_ids) + SECONDARY_INSTANCE_TYPE = var.secondary_instance_type } } diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index 0abd518684..9ee35fc69e 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -63,6 +63,12 @@ variable "instance_type" { default = "m5.large" } +variable "secondary_instance_type" { + description = "(optional) Secondary instance type to avoid no Spot capacity available error" + type = string + default = "m5.xlarge" +} + variable "ami_filter" { description = "List of maps used to create the AMI filter for the action runner AMI." type = map(list(string))