Skip to content

feat(scale-down): Update Owner Logic #1065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Aug 31, 2021
10 changes: 10 additions & 0 deletions modules/runners/lambdas/runners/src/scale-runners/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Octokit } from '@octokit/rest';

export type UnboxPromise<T> = T extends Promise<infer U> ? U : T;

export type GhRunners = UnboxPromise<ReturnType<Octokit['actions']['listSelfHostedRunnersForRepo']>>['data']['runners'];

export class githubCache {
static clients: Map<string, Octokit> = new Map();
static runners: Map<string, GhRunners> = new Map();
}
46 changes: 26 additions & 20 deletions modules/runners/lambdas/runners/src/scale-runners/runners.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { listRunners, createRunner, terminateRunner, RunnerInfo } from './runners';
import { listEC2Runners, createRunner, terminateRunner, RunnerInfo } from './runners';

const mockEC2 = { describeInstances: jest.fn(), runInstances: jest.fn(), terminateInstances: jest.fn() };
const mockSSM = { putParameter: jest.fn() };
Expand All @@ -25,17 +25,17 @@ describe('list instances', () => {
LaunchTime: new Date('2020-10-10T14:48:00.000+09:00'),
InstanceId: 'i-1234',
Tags: [
{ Key: 'Repo', Value: 'CoderToCat/hello-world' },
{ Key: 'Org', Value: 'CoderToCat' },
{ Key: 'Application', Value: 'github-action-runner' },
{ Key: 'Type', Value: 'Org' },
{ Key: 'Owner', Value: 'CoderToCat' },
],
},
{
LaunchTime: new Date('2020-10-11T14:48:00.000+09:00'),
InstanceId: 'i-5678',
Tags: [
{ Key: 'Repo', Value: REPO_NAME },
{ Key: 'Org', Value: ORG_NAME },
{ Key: 'Owner', Value: REPO_NAME },
{ Key: 'Type', Value: 'Repo' },
{ Key: 'Application', Value: 'github-action-runner' },
],
},
Expand All @@ -47,51 +47,53 @@ describe('list instances', () => {
});

it('returns a list of instances', async () => {
const resp = await listRunners();
const resp = await listEC2Runners();
expect(resp.length).toBe(2);
expect(resp).toContainEqual({
instanceId: 'i-1234',
launchTime: new Date('2020-10-10T14:48:00.000+09:00'),
repo: 'CoderToCat/hello-world',
org: 'CoderToCat',
type: 'Org',
owner: 'CoderToCat',
});
expect(resp).toContainEqual({
instanceId: 'i-5678',
launchTime: new Date('2020-10-11T14:48:00.000+09:00'),
repo: REPO_NAME,
org: ORG_NAME,
type: 'Repo',
owner: REPO_NAME,
});
});

it('calls EC2 describe instances', async () => {
await listRunners();
await listEC2Runners();
expect(mockEC2.describeInstances).toBeCalled();
});

it('filters instances on repo name', async () => {
await listRunners({ runnerType: 'Repo', runnerOwner: REPO_NAME, environment: undefined });
await listEC2Runners({ runnerType: 'Repo', runnerOwner: REPO_NAME, environment: undefined });
expect(mockEC2.describeInstances).toBeCalledWith({
Filters: [
{ Name: 'tag:Application', Values: ['github-action-runner'] },
{ Name: 'instance-state-name', Values: ['running', 'pending'] },
{ Name: 'tag:Repo', Values: [REPO_NAME] },
{ Name: 'tag:Type', Values: ['Repo'] },
{ Name: 'tag:Owner', Values: [REPO_NAME] },
],
});
});

it('filters instances on org name', async () => {
await listRunners({ runnerType: 'Org', runnerOwner: ORG_NAME, environment: undefined });
await listEC2Runners({ runnerType: 'Org', runnerOwner: ORG_NAME, environment: undefined });
expect(mockEC2.describeInstances).toBeCalledWith({
Filters: [
{ Name: 'tag:Application', Values: ['github-action-runner'] },
{ Name: 'instance-state-name', Values: ['running', 'pending'] },
{ Name: 'tag:Org', Values: [ORG_NAME] },
{ Name: 'tag:Type', Values: ['Org'] },
{ Name: 'tag:Owner', Values: [ORG_NAME] },
],
});
});

it('filters instances on org name', async () => {
await listRunners({ environment: ENVIRONMENT });
it('filters instances on environment', async () => {
await listEC2Runners({ environment: ENVIRONMENT });
expect(mockEC2.describeInstances).toBeCalledWith({
Filters: [
{ Name: 'tag:Application', Values: ['github-action-runner'] },
Expand All @@ -112,8 +114,10 @@ describe('terminate runner', () => {
it('calls terminate instances with the right instance ids', async () => {
const runner: RunnerInfo = {
instanceId: 'instance-2',
owner: 'owner-2',
type: 'Repo',
};
await terminateRunner(runner);
await terminateRunner(runner.instanceId);

expect(mockEC2.terminateInstances).toBeCalledWith({ InstanceIds: [runner.instanceId] });
});
Expand Down Expand Up @@ -156,7 +160,8 @@ describe('create runner', () => {
ResourceType: 'instance',
Tags: [
{ Key: 'Application', Value: 'github-action-runner' },
{ Key: 'Repo', Value: REPO_NAME },
{ Key: 'Type', Value: 'Repo' },
{ Key: 'Owner', Value: REPO_NAME },
],
},
],
Expand All @@ -183,7 +188,8 @@ describe('create runner', () => {
ResourceType: 'instance',
Tags: [
{ Key: 'Application', Value: 'github-action-runner' },
{ Key: 'Org', Value: ORG_NAME },
{ Key: 'Type', Value: 'Org' },
{ Key: 'Owner', Value: ORG_NAME },
],
},
],
Expand Down
36 changes: 23 additions & 13 deletions modules/runners/lambdas/runners/src/scale-runners/runners.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { EC2, SSM } from 'aws-sdk';

export interface RunnerInfo {
export interface RunnerList {
instanceId: string;
launchTime?: Date;
owner?: string;
type?: string;
repo?: string;
org?: string;
}

export interface RunnerInfo {
instanceId: string;
launchTime?: Date;
owner: string;
type: string;
}

export interface ListRunnerFilters {
runnerType?: 'Org' | 'Repo';
runnerOwner?: string;
Expand All @@ -20,7 +29,7 @@ export interface RunnerInputParameters {
runnerOwner: string;
}

export async function listRunners(filters: ListRunnerFilters | undefined = undefined): Promise<RunnerInfo[]> {
export async function listEC2Runners(filters: ListRunnerFilters | undefined = undefined): Promise<RunnerList[]> {
const ec2 = new EC2();
const ec2Filters = [
{ Name: 'tag:Application', Values: ['github-action-runner'] },
Expand All @@ -31,20 +40,23 @@ export async function listRunners(filters: ListRunnerFilters | undefined = undef
ec2Filters.push({ Name: 'tag:Environment', Values: [filters.environment] });
}
if (filters.runnerType && filters.runnerOwner) {
ec2Filters.push({ Name: `tag:${filters.runnerType}`, Values: [filters.runnerOwner] });
ec2Filters.push({ Name: `tag:Type`, Values: [filters.runnerType] });
ec2Filters.push({ Name: `tag:Owner`, Values: [filters.runnerOwner] });
}
}
const runningInstances = await ec2.describeInstances({ Filters: ec2Filters }).promise();
const runners: RunnerInfo[] = [];
const runners: RunnerList[] = [];
if (runningInstances.Reservations) {
for (const r of runningInstances.Reservations) {
if (r.Instances) {
for (const i of r.Instances) {
runners.push({
instanceId: i.InstanceId as string,
launchTime: i.LaunchTime,
repo: i.Tags?.find((e) => e.Key === 'Repo')?.Value,
org: i.Tags?.find((e) => e.Key === 'Org')?.Value,
owner: i.Tags?.find((e) => e.Key === 'Owner')?.Value as string,
type: i.Tags?.find((e) => e.Key === 'Type')?.Value as string,
repo: i.Tags?.find((e) => e.Key === 'Repo')?.Value as string,
org: i.Tags?.find((e) => e.Key === 'Org')?.Value as string,
});
}
}
Expand All @@ -53,14 +65,14 @@ export async function listRunners(filters: ListRunnerFilters | undefined = undef
return runners;
}

export async function terminateRunner(runner: RunnerInfo): Promise<void> {
export async function terminateRunner(instanceId: string): Promise<void> {
const ec2 = new EC2();
await ec2
.terminateInstances({
InstanceIds: [runner.instanceId],
InstanceIds: [instanceId],
})
.promise();
console.debug('Runner terminated.' + runner.instanceId);
console.debug(`Runner ${instanceId} has been terminated.`);
}

export async function createRunner(runnerParameters: RunnerInputParameters, launchTemplateName: string): Promise<void> {
Expand Down Expand Up @@ -99,10 +111,8 @@ function getInstanceParams(
ResourceType: 'instance',
Tags: [
{ Key: 'Application', Value: 'github-action-runner' },
{
Key: runnerParameters.runnerType,
Value: runnerParameters.runnerOwner,
},
{ Key: 'Type', Value: runnerParameters.runnerType },
{ Key: 'Owner', Value: runnerParameters.runnerOwner },
],
},
],
Expand Down
Loading