Skip to content

Commit 52efdbc

Browse files
authored
Feature/runners lambdas wip npalm (#9)
* Create runners * Fix user_data, refactor * Connect create instance to lambda, update policies, minor fixes
1 parent 292faa1 commit 52efdbc

File tree

12 files changed

+112
-40
lines changed

12 files changed

+112
-40
lines changed

examples/default/main.tf

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,25 @@ resource "random_password" "random" {
88
length = 32
99
}
1010

11-
1211
module "runners" {
1312
source = "../../"
1413

1514
aws_region = local.aws_region
1615
vpc_id = module.vpc.vpc_id
16+
subnet_ids = module.vpc.private_subnets
1717

1818
environment = local.environment
1919
tags = {
2020
Project = "ProjectX"
2121
}
2222

23-
github_app_webhook_secret = var.github_app_webhook_secret
24-
25-
github_app_client_id = var.github_app_client_id
26-
github_app_client_secret = var.github_app_client_secret
27-
github_app_id = var.github_app_id
28-
github_app_key_base64 = var.github_app_key_base64
23+
github_app_client_id = var.github_app_client_id
24+
github_app_client_secret = var.github_app_client_secret
25+
github_app_id = var.github_app_id
26+
github_app_key_base64 = var.github_app_key_base64
27+
github_app_webhook_secret = random_password.random.result
2928

30-
enable_organization_runners = var.enable_organization_runners
29+
enable_organization_runners = false
3130
}
3231

3332

examples/default/outputs.tf

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,9 @@ output "runners" {
44
}
55
}
66

7-
# output "binaries_syncer" {
8-
# value = {
9-
# binaries_syncer = module.runners.binaries_syncer
10-
# }
11-
# }
12-
137
output "webhook" {
148
value = {
9+
secret = random_password.random.result
1510
gateway = module.runners.webhook.gateway
1611
}
1712
}

examples/default/variables.tf

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
variable "enable_organization_runners" {
2-
type = bool
3-
}
41

52
variable "github_app_key_base64" {}
63

@@ -10,4 +7,3 @@ variable "github_app_client_id" {}
107

118
variable "github_app_client_secret" {}
129

13-
variable "github_app_webhook_secret" {}

main.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ module "runners" {
3838

3939
aws_region = var.aws_region
4040
vpc_id = var.vpc_id
41+
subnet_ids = var.subnet_ids
4142
environment = var.environment
4243
tags = local.tags
4344

modules/runners/lambdas/scale-runners/src/scale-runners/handler.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createAppAuth } from '@octokit/auth-app';
22
import { Octokit } from '@octokit/rest';
33
import { AppAuth } from '@octokit/auth-app/dist-types/types';
4-
import { listRunners } from './runners';
4+
import { listRunners, createRunner } from './runners';
55
import yn from 'yn';
66

77
export interface ActionRequestMessage {
@@ -34,8 +34,9 @@ async function createInstallationClient(githubAppAuth: AppAuth): Promise<Octokit
3434

3535
export const handle = async (eventSource: string, payload: ActionRequestMessage): Promise<void> => {
3636
if (eventSource !== 'aws:sqs') throw Error('Cannot handle non-SQS events!');
37-
const enableOrgLevel = yn(process.env.ENABLE_ORGANIZATION_RUNNERS);
37+
const enableOrgLevel = yn(process.env.ENABLE_ORGANIZATION_RUNNERS, { default: true });
3838
const maximumRunners = parseInt(process.env.RUNNERS_MAXIMUM_COUNT || '3');
39+
const environment = process.env.ENVIRONMENT as string;
3940
const githubAppAuth = createGithubAppAuth(payload.installationId);
4041
const githubInstallationClient = await createInstallationClient(githubAppAuth);
4142
const queuedWorkflows = await githubInstallationClient.actions.listRepoWorkflowRuns({
@@ -70,7 +71,17 @@ export const handle = async (eventSource: string, payload: ActionRequestMessage)
7071
repo: payload.repositoryName,
7172
});
7273
const token = registrationToken.data.token;
73-
// create runner
74+
75+
await createRunner({
76+
environment: environment,
77+
runnerConfig: enableOrgLevel
78+
? `--url https://github.com/${payload.repositoryOwner} --token ${token}`
79+
: `--url https://github.com/${payload.repositoryOwner}/${payload.repositoryName} --token ${token}`,
80+
orgName: enableOrgLevel ? payload.repositoryOwner : undefined,
81+
repoName: enableOrgLevel ? undefined : `${payload.repositoryOwner}/${payload.repositoryName}`,
82+
});
83+
} else {
84+
console.info('No runner will be created, maximum number of runners reached.');
7485
}
7586
}
7687
};

modules/runners/lambdas/scale-runners/src/scale-runners/runners.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { EC2 } from 'aws-sdk';
1+
import { EC2, SSM } from 'aws-sdk';
22

33
export interface RunnerInfo {
44
instanceId: string;
@@ -44,3 +44,61 @@ export async function listRunners(filters: ListRunnerFilters | undefined = undef
4444
}
4545
return runners;
4646
}
47+
48+
export interface RunnerInputParameters {
49+
runnerConfig: string;
50+
environment: string;
51+
repoName?: string;
52+
orgName?: string;
53+
}
54+
55+
export async function createRunner(runnerParameters: RunnerInputParameters): Promise<void> {
56+
const launchTemplateName = process.env.LAUNCH_TEMPLATE_NAME as string;
57+
const launchTemplateVersion = process.env.LAUNCH_TEMPLATE_VERSION as string;
58+
59+
const subnets = (process.env.SUBNET_IDS as string).split(',');
60+
const randomSubnet = subnets[Math.floor(Math.random() * subnets.length)];
61+
62+
const ec2 = new EC2();
63+
const runInstancesResponse = await ec2
64+
.runInstances({
65+
MaxCount: 1,
66+
MinCount: 1,
67+
LaunchTemplate: {
68+
LaunchTemplateName: launchTemplateName,
69+
Version: launchTemplateVersion,
70+
},
71+
SubnetId: randomSubnet,
72+
TagSpecifications: [
73+
{
74+
ResourceType: 'instance',
75+
Tags: [
76+
{ Key: 'Application', Value: 'github-action-runner' },
77+
{
78+
Key: runnerParameters.orgName ? 'Org' : 'Repo',
79+
Value: runnerParameters.orgName ? runnerParameters.orgName : runnerParameters.repoName,
80+
},
81+
],
82+
},
83+
],
84+
})
85+
.promise();
86+
console.info(
87+
'Created instance(s): ',
88+
runInstancesResponse.Instances?.forEach((i: EC2.Instance) => {
89+
i.InstanceId;
90+
}),
91+
);
92+
93+
const ssm = new SSM();
94+
runInstancesResponse.Instances?.forEach((i: EC2.Instance) => {
95+
const r = ssm
96+
.putParameter({
97+
Name: runnerParameters.environment + '-' + (i.InstanceId as string),
98+
Value: runnerParameters.runnerConfig,
99+
Type: 'SecureString',
100+
})
101+
.promise();
102+
console.log(r);
103+
});
104+
}

modules/runners/main.tf

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
locals {
2-
name_sg = var.overrides["name_sg"] == "" ? local.tags["Name"] : var.overrides["name_sg"]
32

43
tags = merge(
54
{
6-
"Name" = format("%s", var.environment)
5+
"Name" = format("%s-action-runner", var.environment)
76
},
87
{
98
"Environment" = format("%s", var.environment)
109
},
1110
var.tags,
1211
)
12+
13+
name_sg = var.overrides["name_sg"] == "" ? local.tags["Name"] : var.overrides["name_sg"]
14+
name_runner = var.overrides["name_runner"] == "" ? local.tags["Name"] : var.overrides["name_runner"]
15+
1316
}
1417

1518
data "aws_ami" "runner" {

modules/runners/policies/lambda-scale-up.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
"Effect": "Allow",
1616
"Action": "iam:PassRole",
1717
"Resource": "${arn_runner_instance_role}"
18+
},
19+
{
20+
"Effect": "Allow",
21+
"Action": ["ssm:PutParameter"],
22+
"Resource": "*"
1823
}
1924
]
2025
}

modules/runners/scale-runners-lambda.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ resource "aws_lambda_function" "scale_runners_lambda" {
55
role = aws_iam_role.scale_runners_lambda.arn
66
handler = "index.handler"
77
runtime = "nodejs12.x"
8+
timeout = 60
89

910
environment {
1011
variables = {
@@ -13,6 +14,10 @@ resource "aws_lambda_function" "scale_runners_lambda" {
1314
GITHUB_APP_ID = var.github_app_id
1415
GITHUB_APP_CLIENT_ID = var.github_app_client_id
1516
GITHUB_APP_CLIENT_SECRET = var.github_app_client_secret
17+
SUBNET_IDS = join(",", var.subnet_ids)
18+
LAUNCH_TEMPLATE_NAME = aws_launch_template.runner.name
19+
LAUNCH_TEMPLATE_VERSION = aws_launch_template.runner.latest_version
20+
ENVIRONMENT = var.environment
1621
}
1722
}
1823
}

modules/runners/templates/user-data.sh

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,17 @@ amazon-linux-extras install docker
88
service docker start
99
usermod -a -G docker ec2-user
1010

11-
# Install runner
1211
yum install -y curl jq git
13-
cd /home/ec2-user
14-
mkdir actions-runner && cd actions-runner
15-
#!/bin/bash -ex
16-
exec > >(tee /var/log/user-data.log | logger -t user-data -s 2>/dev/console) 2>&1
17-
18-
yum update -y
1912

2013
${pre_install}
2114

22-
# Install docker
23-
amazon-linux-extras install docker
24-
service docker start
25-
usermod -a -G docker ec2-user
26-
2715
# Install runner
28-
yum install -y curl jq git
29-
3016
cd /home/ec2-user
3117
mkdir actions-runner && cd actions-runner
18+
3219
aws s3 cp ${s3_location_runner_distribution} actions-runner.tar.gz
3320
tar xzf ./actions-runner.tar.gz
34-
rm actions-runner.tar.gz
21+
rm -rf actions-runner.tar.gz
3522

3623
INSTANCE_ID=$(wget -q -O - http://169.254.169.254/latest/meta-data/instance-id)
3724
REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)

modules/runners/variables.tf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@ variable "vpc_id" {
88
type = string
99
}
1010

11+
variable "subnet_ids" {
12+
description = "List of subnets in which the action runners will be launched, the subnets needs to be subnets in the `vpc_id`."
13+
type = list(string)
14+
}
15+
1116
variable "overrides" {
1217
description = "This maps provides the possibility to override some defaults. The following attributes are supported: `name_sg` overwrite the `Name` tag for all security groups created by this module. `name_runner_agent_instance` override the `Name` tag for the ec2 instance defined in the auto launch configuration. `name_docker_machine_runners` ovverrid the `Name` tag spot instances created by the runner agent."
1318
type = map(string)
1419

1520
default = {
16-
name_sg = ""
21+
name_runner = ""
22+
name_sg = ""
1723
}
1824
}
1925

variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ variable "vpc_id" {
88
type = string
99
}
1010

11+
variable "subnet_ids" {
12+
description = "List of subnets in which the action runners will be launched, the subnets needs to be subnets in the `vpc_id`."
13+
type = list(string)
14+
}
15+
16+
1117
variable "tags" {
1218
description = "Map of tags that will be added to created resources. By default resources will be tagged with name and environment."
1319
type = map(string)

0 commit comments

Comments
 (0)