@cloudsnorkel/cdk-github-runners
Version:
CDK construct to create GitHub Actions self-hosted runners. Creates ephemeral runners on demand. Easy to deploy and highly customizable.
139 lines • 18.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.handler = handler;
const client_sfn_1 = require("@aws-sdk/client-sfn");
const lambda_github_1 = require("./lambda-github");
const sfn = new client_sfn_1.SFNClient();
async function handler(event) {
const result = { batchItemFailures: [] };
const octokitCache = new Map();
for (const record of event.Records) {
const input = JSON.parse(record.body);
console.log({
notice: 'Checking runner',
input,
});
const retryLater = () => result.batchItemFailures.push({ itemIdentifier: record.messageId });
// check if step function is still running
const execution = await sfn.send(new client_sfn_1.DescribeExecutionCommand({ executionArn: input.executionArn }));
if (execution.status != 'RUNNING') {
// no need to test again as runner already finished
console.log({
notice: 'Runner already finished',
input,
});
continue;
}
// get github access
let octokit;
let secrets;
const cached = octokitCache.get(input.installationId);
if (cached) {
// use cached octokit
octokit = cached.octokit;
secrets = cached.secrets;
}
else {
// getOctokit calls secrets manager and Github API every time, so cache the result
// this handler can work on multiple runners at once, so caching is important
const { octokit: newOctokit, githubSecrets: newSecrets } = await (0, lambda_github_1.getOctokit)(input.installationId);
octokit = newOctokit;
secrets = newSecrets;
octokitCache.set(input.installationId, { octokit, secrets });
}
// find runner
const runner = await (0, lambda_github_1.getRunner)(octokit, secrets.runnerLevel, input.owner, input.repo, input.runnerName);
if (!runner) {
console.log({
notice: 'Runner not running yet',
input,
});
retryLater();
continue;
}
// if not idle, try again later
// we want to try again because the runner might be retried due to e.g. lambda timeout
// we need to keep following the retry too and make sure it doesn't go idle
if (runner.busy) {
console.log({
notice: 'Runner is not idle',
input,
});
retryLater();
continue;
}
// check if max idle timeout has reached
let found = false;
for (const label of runner.labels) {
if (label.name.toLowerCase().startsWith('cdkghr:started:')) {
const started = parseFloat(label.name.split(':')[2]);
const startedDate = new Date(started * 1000);
const now = new Date();
const diffMs = now.getTime() - startedDate.getTime();
console.log({
notice: `Runner ${input.runnerName} started ${diffMs / 1000} seconds ago`,
input,
});
if (diffMs > 1000 * input.maxIdleSeconds) {
// max idle time reached, delete runner
console.log({
notice: `Runner ${input.runnerName} is idle for too long`,
input,
});
try {
// stop step function first, so it's marked as aborted with the proper error
// if we delete the runner first, the step function will be marked as failed with a generic error
console.log({
notice: `Stopping step function ${input.executionArn}...`,
input,
});
await sfn.send(new client_sfn_1.StopExecutionCommand({
executionArn: input.executionArn,
error: 'IdleRunner',
cause: `Runner ${input.runnerName} on ${input.owner}/${input.repo} is idle for too long (${diffMs / 1000} seconds and limit is ${input.maxIdleSeconds} seconds)`,
}));
}
catch (e) {
console.error({
notice: `Failed to stop step function ${input.executionArn}: ${e}`,
input,
});
retryLater();
continue;
}
try {
console.log({
notice: `Deleting runner ${runner.id}...`,
input,
});
await (0, lambda_github_1.deleteRunner)(octokit, secrets.runnerLevel, input.owner, input.repo, runner.id);
}
catch (e) {
console.error({
notice: `Failed to delete runner ${runner.id}: ${e}`,
input,
});
retryLater();
continue;
}
}
else {
// still idle, timeout not reached -- retry later
retryLater();
}
found = true;
break;
}
}
if (!found) {
// no started label? retry later (it won't retry forever as eventually the runner will stop and the step function will finish)
console.error({
notice: 'No `cdkghr:started:xxx` label found???',
input,
});
retryLater();
}
}
return result;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWRsZS1ydW5uZXItcmVwZWFyLmxhbWJkYS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pZGxlLXJ1bm5lci1yZXBlYXIubGFtYmRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBZ0JBLDBCQTZJQztBQTdKRCxvREFBZ0c7QUFHaEcsbURBQXFGO0FBV3JGLE1BQU0sR0FBRyxHQUFHLElBQUksc0JBQVMsRUFBRSxDQUFDO0FBRXJCLEtBQUssVUFBVSxPQUFPLENBQUMsS0FBeUI7SUFDckQsTUFBTSxNQUFNLEdBQStCLEVBQUUsaUJBQWlCLEVBQUUsRUFBRSxFQUFFLENBQUM7SUFDckUsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQW9FLENBQUM7SUFFakcsS0FBSyxNQUFNLE1BQU0sSUFBSSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbkMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUEwQixDQUFDO1FBQy9ELE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDVixNQUFNLEVBQUUsaUJBQWlCO1lBQ3pCLEtBQUs7U0FDTixDQUFDLENBQUM7UUFFSCxNQUFNLFVBQVUsR0FBRyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQUUsY0FBYyxFQUFFLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBRTdGLDBDQUEwQztRQUMxQyxNQUFNLFNBQVMsR0FBRyxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxxQ0FBd0IsQ0FBQyxFQUFFLFlBQVksRUFBRSxLQUFLLENBQUMsWUFBWSxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3JHLElBQUksU0FBUyxDQUFDLE1BQU0sSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNsQyxtREFBbUQ7WUFDbkQsT0FBTyxDQUFDLEdBQUcsQ0FBQztnQkFDVixNQUFNLEVBQUUseUJBQXlCO2dCQUNqQyxLQUFL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LEVBQUU7NEJBQ3BELEtBQUs7eUJBQ04sQ0FBQyxDQUFDO3dCQUNILFVBQVUsRUFBRSxDQUFDO3dCQUNiLFNBQVM7b0JBQ1gsQ0FBQztnQkFDSCxDQUFDO3FCQUFNLENBQUM7b0JBQ04saURBQWlEO29CQUNqRCxVQUFVLEVBQUUsQ0FBQztnQkFDZixDQUFDO2dCQUVELEtBQUssR0FBRyxJQUFJLENBQUM7Z0JBQ2IsTUFBTTtZQUNSLENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsOEhBQThIO1lBQzlILE9BQU8sQ0FBQyxLQUFLLENBQUM7Z0JBQ1osTUFBTSxFQUFFLHdDQUF3QztnQkFDaEQsS0FBSzthQUNOLENBQUMsQ0FBQztZQUNILFVBQVUsRUFBRSxDQUFDO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRGVzY3JpYmVFeGVjdXRpb25Db21tYW5kLCBTRk5DbGllbnQsIFN0b3BFeGVjdXRpb25Db21tYW5kIH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXNmbic7XG5pbXBvcnQgeyBPY3Rva2l0IH0gZnJvbSAnQG9jdG9raXQvcmVzdCc7XG5pbXBvcnQgKiBhcyBBV1NMYW1iZGEgZnJvbSAnYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBkZWxldGVSdW5uZXIsIGdldE9jdG9raXQsIGdldFJ1bm5lciwgR2l0SHViU2VjcmV0cyB9IGZyb20gJy4vbGFtYmRhLWdpdGh1Yic7XG5cbmludGVyZmFjZSBJZGxlUmVhcGVyTGFtYmRhSW5wdXQge1xuICByZWFkb25seSBleGVjdXRpb25Bcm46IHN0cmluZztcbiAgcmVhZG9ubHkgcnVubmVyTmFtZTogc3RyaW5nO1xuICByZWFkb25seSBvd25lcjogc3RyaW5nO1xuICByZWFkb25seSByZXBvOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGluc3RhbGxhdGlvbklkPzogbnVtYmVyO1xuICByZWFkb25seSBtYXhJZGxlU2Vjb25kczogbnVtYmVyO1xufVxuXG5jb25zdCBzZm4gPSBuZXcgU0ZOQ2xpZW50KCk7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuU1FTRXZlbnQpOiBQcm9taXNlPEFXU0xhbWJkYS5TUVNCYXRjaFJlc3BvbnNlPiB7XG4gIGNvbnN0IHJlc3VsdDogQVdTTGFtYmRhLlNRU0JhdGNoUmVzcG9uc2UgPSB7IGJhdGNoSXRlbUZhaWx1cmVzOiBbXSB9O1xuICBjb25zdCBvY3Rva2l0Q2FjaGUgPSBuZXcgTWFwPG51bWJlciB8IHVuZGVmaW5lZCwgeyBvY3Rva2l0OiBPY3Rva2l0OyBzZWNyZXRzOiBHaXRIdWJTZWNyZXRzIH0+KCk7XG5cbiAgZm9yIChjb25zdCByZWNvcmQgb2YgZXZlbnQuUmVjb3Jkcykge1xuICAgIGNvbnN0IGlucHV0ID0gSlNPTi5wYXJzZShyZWNvcmQuYm9keSkgYXMgSWRsZVJlYXBlckxhbWJkYUlucHV0O1xuICAgIGNvbnNvbGUubG9nKHtcbiAgICAgIG5vdGljZTogJ0NoZWNraW5nIHJ1bm5lcicsXG4gICAgICBpbnB1dCxcbiAgICB9KTtcblxuICAgIGNvbnN0IHJldHJ5TGF0ZXIgPSAoKSA9PiByZXN1bHQuYmF0Y2hJdGVtRmFpbHVyZXMucHVzaCh7IGl0ZW1JZGVudGlmaWVyOiByZWNvcmQubWVzc2FnZUlkIH0pO1xuXG4gICAgLy8gY2hlY2sgaWYgc3RlcCBmdW5jdGlvbiBpcyBzdGlsbCBydW5uaW5nXG4gICAgY29uc3QgZXhlY3V0aW9uID0gYXdhaXQgc2ZuLnNlbmQobmV3IERlc2NyaWJlRXhlY3V0aW9uQ29tbWFuZCh7IGV4ZWN1dGlvbkFybjogaW5wdXQuZXhlY3V0aW9uQXJuIH0pKTtcbiAgICBpZiAoZXhlY3V0aW9uLnN0YXR1cyAhPSAnUlVOTklORycpIHtcbiAgICAgIC8vIG5vIG5lZWQgdG8gdGVzdCBhZ2FpbiBhcyBydW5uZXIgYWxyZWFkeSBmaW5pc2hlZFxuICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICBub3RpY2U6ICdSdW5uZXIgYWxyZWFkeSBmaW5pc2hlZCcsXG4gICAgICAgIGlucHV0LFxuICAgICAgfSk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvLyBnZXQgZ2l0aHViIGFjY2Vzc1xuICAgIGxldCBvY3Rva2l0OiBPY3Rva2l0O1xuICAgIGxldCBzZWNyZXRzOiBHaXRIdWJTZWNyZXRzO1xuICAgIGNvbnN0IGNhY2hlZCA9IG9jdG9raXRDYWNoZS5nZXQoaW5wdXQuaW5zdGFsbGF0aW9uSWQpO1xuICAgIGlmIChjYWNoZWQpIHtcbiAgICAgIC8vIHVzZSBjYWNoZWQgb2N0b2tpdFxuICAgICAgb2N0b2tpdCA9IGNhY2hlZC5vY3Rva2l0O1xuICAgICAgc2VjcmV0cyA9IGNhY2hlZC5zZWNyZXRzO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBnZXRPY3Rva2l0IGNhbGxzIHNlY3JldHMgbWFuYWdlciBhbmQgR2l0aHViIEFQSSBldmVyeSB0aW1lLCBzbyBjYWNoZSB0aGUgcmVzdWx0XG4gICAgICAvLyB0aGlzIGhhbmRsZXIgY2FuIHdvcmsgb24gbXVsdGlwbGUgcnVubmVycyBhdCBvbmNlLCBzbyBjYWNoaW5nIGlzIGltcG9ydGFudFxuICAgICAgY29uc3QgeyBvY3Rva2l0OiBuZXdPY3Rva2l0LCBnaXRodWJTZWNyZXRzOiBuZXdTZWNyZXRzIH0gPSBhd2FpdCBnZXRPY3Rva2l0KGlucHV0Lmluc3RhbGxhdGlvbklkKTtcbiAgICAgIG9jdG9raXQgPSBuZXdPY3Rva2l0O1xuICAgICAgc2VjcmV0cyA9IG5ld1NlY3JldHM7XG4gICAgICBvY3Rva2l0Q2FjaGUuc2V0KGlucHV0Lmluc3RhbGxhdGlvbklkLCB7IG9jdG9raXQsIHNlY3JldHMgfSk7XG4gICAgfVxuXG4gICAgLy8gZmluZCBydW5uZXJcbiAgICBjb25zdCBydW5uZXIgPSBhd2FpdCBnZXRSdW5uZXIob2N0b2tpdCwgc2VjcmV0cy5ydW5uZXJMZXZlbCwgaW5wdXQub3duZXIsIGlucHV0LnJlcG8sIGlucHV0LnJ1bm5lck5hbWUpO1xuICAgIGlmICghcnVubmVyKSB7XG4gICAgICBjb25zb2xlLmxvZyh7XG4gICAgICAgIG5vdGljZTogJ1J1bm5lciBub3QgcnVubmluZyB5ZXQnLFxuICAgICAgICBpbnB1dCxcbiAgICAgIH0pO1xuICAgICAgcmV0cnlMYXRlcigpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gaWYgbm90IGlkbGUsIHRyeSBhZ2FpbiBsYXRlclxuICAgIC8vIHdlIHdhbnQgdG8gdHJ5IGFnYWluIGJlY2F1c2UgdGhlIHJ1bm5lciBtaWdodCBiZSByZXRyaWVkIGR1ZSB0byBlLmcuIGxhbWJkYSB0aW1lb3V0XG4gICAgLy8gd2UgbmVlZCB0byBrZWVwIGZvbGxvd2luZyB0aGUgcmV0cnkgdG9vIGFuZCBtYWtlIHN1cmUgaXQgZG9lc24ndCBnbyBpZGxlXG4gICAgaWYgKHJ1bm5lci5idXN5KSB7XG4gICAgICBjb25zb2xlLmxvZyh7XG4gICAgICAgIG5vdGljZTogJ1J1bm5lciBpcyBub3QgaWRsZScsXG4gICAgICAgIGlucHV0LFxuICAgICAgfSk7XG4gICAgICByZXRyeUxhdGVyKCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvLyBjaGVjayBpZiBtYXggaWRsZSB0aW1lb3V0IGhhcyByZWFjaGVkXG4gICAgbGV0IGZvdW5kID0gZmFsc2U7XG4gICAgZm9yIChjb25zdCBsYWJlbCBvZiBydW5uZXIubGFiZWxzKSB7XG4gICAgICBpZiAobGFiZWwubmFtZS50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgoJ2Nka2docjpzdGFydGVkOicpKSB7XG4gICAgICAgIGNvbnN0IHN0YXJ0ZWQgPSBwYXJzZUZsb2F0KGxhYmVsLm5hbWUuc3BsaXQoJzonKVsyXSk7XG4gICAgICAgIGNvbnN0IHN0YXJ0ZWREYXRlID0gbmV3IERhdGUoc3RhcnRlZCAqIDEwMDApO1xuICAgICAgICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuICAgICAgICBjb25zdCBkaWZmTXMgPSBub3cuZ2V0VGltZSgpIC0gc3RhcnRlZERhdGUuZ2V0VGltZSgpO1xuXG4gICAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgICBub3RpY2U6IGBSdW5uZXIgJHtpbnB1dC5ydW5uZXJOYW1lfSBzdGFydGVkICR7ZGlmZk1zIC8gMTAwMH0gc2Vjb25kcyBhZ29gLFxuICAgICAgICAgIGlucHV0LFxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoZGlmZk1zID4gMTAwMCAqIGlucHV0Lm1heElkbGVTZWNvbmRzKSB7XG4gICAgICAgICAgLy8gbWF4IGlkbGUgdGltZSByZWFjaGVkLCBkZWxldGUgcnVubmVyXG4gICAgICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICAgICAgbm90aWNlOiBgUnVubmVyICR7aW5wdXQucnVubmVyTmFtZX0gaXMgaWRsZSBmb3IgdG9vIGxvbmdgLFxuICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gc3RvcCBzdGVwIGZ1bmN0aW9uIGZpcnN0LCBzbyBpdCdzIG1hcmtlZCBhcyBhYm9ydGVkIHdpdGggdGhlIHByb3BlciBlcnJvclxuICAgICAgICAgICAgLy8gaWYgd2UgZGVsZXRlIHRoZSBydW5uZXIgZmlyc3QsIHRoZSBzdGVwIGZ1bmN0aW9uIHdpbGwgYmUgbWFya2VkIGFzIGZhaWxlZCB3aXRoIGEgZ2VuZXJpYyBlcnJvclxuICAgICAgICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICAgICAgICBub3RpY2U6IGBTdG9wcGluZyBzdGVwIGZ1bmN0aW9uICR7aW5wdXQuZXhlY3V0aW9uQXJufS4uLmAsXG4gICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBhd2FpdCBzZm4uc2VuZChuZXcgU3RvcEV4ZWN1dGlvbkNvbW1hbmQoe1xuICAgICAgICAgICAgICBleGVjdXRpb25Bcm46IGlucHV0LmV4ZWN1dGlvbkFybixcbiAgICAgICAgICAgICAgZXJyb3I6ICdJZGxlUnVubmVyJyxcbiAgICAgICAgICAgICAgY2F1c2U6IGBSdW5uZXIgJHtpbnB1dC5ydW5uZXJOYW1lfSBvbiAke2lucHV0Lm93bmVyfS8ke2lucHV0LnJlcG99IGlzIGlkbGUgZm9yIHRvbyBsb25nICgke2RpZmZNcyAvIDEwMDB9IHNlY29uZHMgYW5kIGxpbWl0IGlzICR7aW5wdXQubWF4SWRsZVNlY29uZHN9IHNlY29uZHMpYCxcbiAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKHtcbiAgICAgICAgICAgICAgbm90aWNlOiBgRmFpbGVkIHRvIHN0b3Agc3RlcCBmdW5jdGlvbiAke2lucHV0LmV4ZWN1dGlvbkFybn06ICR7ZX1gLFxuICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0cnlMYXRlcigpO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgICAgICAgbm90aWNlOiBgRGVsZXRpbmcgcnVubmVyICR7cnVubmVyLmlkfS4uLmAsXG4gICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBhd2FpdCBkZWxldGVSdW5uZXIob2N0b2tpdCwgc2VjcmV0cy5ydW5uZXJMZXZlbCwgaW5wdXQub3duZXIsIGlucHV0LnJlcG8sIHJ1bm5lci5pZCk7XG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcih7XG4gICAgICAgICAgICAgIG5vdGljZTogYEZhaWxlZCB0byBkZWxldGUgcnVubmVyICR7cnVubmVyLmlkfTogJHtlfWAsXG4gICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXRyeUxhdGVyKCk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gc3RpbGwgaWRsZSwgdGltZW91dCBub3QgcmVhY2hlZCAtLSByZXRyeSBsYXRlclxuICAgICAgICAgIHJldHJ5TGF0ZXIoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvdW5kID0gdHJ1ZTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFmb3VuZCkge1xuICAgICAgLy8gbm8gc3RhcnRlZCBsYWJlbD8gcmV0cnkgbGF0ZXIgKGl0IHdvbid0IHJldHJ5IGZvcmV2ZXIgYXMgZXZlbnR1YWxseSB0aGUgcnVubmVyIHdpbGwgc3RvcCBhbmQgdGhlIHN0ZXAgZnVuY3Rpb24gd2lsbCBmaW5pc2gpXG4gICAgICBjb25zb2xlLmVycm9yKHtcbiAgICAgICAgbm90aWNlOiAnTm8gYGNka2docjpzdGFydGVkOnh4eGAgbGFiZWwgZm91bmQ/Pz8nLFxuICAgICAgICBpbnB1dCxcbiAgICAgIH0pO1xuICAgICAgcmV0cnlMYXRlcigpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXN1bHQ7XG59XG4iXX0=