Skip to content

Commit

Permalink
WIP: Scale lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
gertjanmaas committed May 6, 2020
1 parent 64ff3ea commit 5facc71
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 8 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/lambda-scale-runners.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Lambda Scale Runners
on:
push:
branches:
- master
pull_request:
paths:
- .github/workflows/lambda-scale-runners.yml
- "modules/runners/lambdas/scale-runners/**"

jobs:
build:
runs-on: ubuntu-latest
container: node:12
defaults:
run:
working-directory: modules/runners/lambdas/scale-runners

steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: yarn install
- name: Run tests
run: yarn test
- name: Build distribution
run: yarn build
5 changes: 4 additions & 1 deletion modules/runners/lambdas/scale-runners/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@
"ts-node-dev": "^1.0.0-pre.44",
"typescript": "^3.8.3"
},
"dependencies": {}
"dependencies": {
"@octokit/auth-app": "^2.4.5",
"@octokit/rest": "^17.6.0"
}
}
11 changes: 7 additions & 4 deletions modules/runners/lambdas/scale-runners/src/lambda.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { handle } from './scale-runners/handler';

module.exports.handler = async (event: any, context: any, callback: any) => {
const statusCode = await handle(event.headers, event.body);
return callback(null, {
statusCode: statusCode,
});
try {
await handle(event.eventSource, JSON.parse(event.body));
return callback(null);
} catch (e) {
console.error(e);
return callback('Failed handling SQS event');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ActionRequestMessage, handle } from './handler';

import { createAppAuth } from '@octokit/auth-app';
import { Octokit } from '@octokit/rest';

jest.mock('@octokit/auth-app', () => ({
createAppAuth: jest.fn().mockImplementation(() => jest.fn().mockImplementation(() => ({ token: 'Blaat' }))),
}));
const mockOctokit = { checks: { get: jest.fn() }, actions: { listRepoWorkflowRuns: jest.fn() } };
jest.mock('@octokit/rest', () => ({
Octokit: jest.fn().mockImplementation(() => mockOctokit),
}));

const TEST_DATA: ActionRequestMessage = {
id: 1,
eventType: 'check_run',
repositoryName: 'hello-world',
repositoryOwner: 'Codertocat',
installationId: 2,
};

describe('handler', () => {
beforeEach(() => {
process.env.GITHUB_APP_KEY = 'TEST_CERTIFICATE_DATA';
process.env.GITHUB_APP_ID = '1337';
process.env.GITHUB_APP_CLIENT_ID = 'TEST_CLIENT_ID';
process.env.GITHUB_APP_CLIENT_SECRET = 'TEST_CLIENT_SECRET';
jest.clearAllMocks();
mockOctokit.actions.listRepoWorkflowRuns.mockImplementation(() => ({
total_count: 1,
}));
});

it('ignores non-sqs events', async () => {
expect.assertions(1);
expect(handle('aws:s3', TEST_DATA)).rejects.toEqual(Error('Cannot handle non-SQS events!'));
});

it('checks queued workflows', async () => {
await handle('aws:sqs', TEST_DATA);
expect(mockOctokit.actions.listRepoWorkflowRuns).toBeCalledWith({
owner: TEST_DATA.repositoryOwner,
repo: TEST_DATA.repositoryName,
status: 'queued',
});
});
});
49 changes: 46 additions & 3 deletions modules/runners/lambdas/scale-runners/src/scale-runners/handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
import { IncomingHttpHeaders } from 'http';
import { createAppAuth } from '@octokit/auth-app';
import { Octokit } from '@octokit/rest';

export const handle = async (headers: IncomingHttpHeaders, payload: any): Promise<number> => {
return 200;
export interface ActionRequestMessage {
id: number;
eventType: string;
repositoryName: string;
repositoryOwner: string;
installationId: number;
}

async function createGithubClient(installationId: number): Promise<Octokit> {
const privateKey = process.env.GITHUB_APP_KEY as string;
const appId: number = parseInt(process.env.GITHUB_APP_ID as string);
const clientId = process.env.GITHUB_APP_CLIENT_ID as string;
const clientSecret = process.env.GITHUB_APP_CLIENT_SECRET as string;

try {
const auth = createAppAuth({
id: appId,
privateKey: privateKey,
installationId: installationId,
clientId: clientId,
clientSecret: clientSecret,
});
const installationAuthentication = await auth({ type: 'installation' });

return new Octokit({
auth: installationAuthentication.token,
});
} catch (e) {
Promise.reject(e);
}
}

export const handle = async (eventSource: string, payload: ActionRequestMessage): Promise<void> => {
if (eventSource !== 'aws:sqs') throw Error('Cannot handle non-SQS events!');
const githubClient = await createGithubClient(payload.installationId);
const queuedWorkflows = await githubClient.actions.listRepoWorkflowRuns({
owner: payload.repositoryOwner,
repo: payload.repositoryName,
// @ts-ignore (typing is incorrect)
status: 'queued',
});
console.info(
`Repo ${payload.repositoryOwner}/${payload.repositoryName} has ${queuedWorkflows.total_count} queued workflow runs`,
);
};

0 comments on commit 5facc71

Please sign in to comment.