@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.
131 lines • 17.6 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: [] };
let octokitCache;
let runnerLevel;
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
if (!octokitCache) {
// getOctokit calls secrets manager every time, so cache the result
const { octokit, githubSecrets } = await (0, lambda_github_1.getOctokit)(input.installationId);
// TODO if installationId changes during normal operations, we may have some records with good installationId, and some with bad
octokitCache = octokit;
runnerLevel = githubSecrets.runnerLevel;
}
// find runner
const runner = await (0, lambda_github_1.getRunner)(octokitCache, 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)(octokitCache, 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWRsZS1ydW5uZXItcmVwZWFyLmxhbWJkYS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pZGxlLXJ1bm5lci1yZXBlYXIubGFtYmRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBZ0JBLDBCQXNJQztBQXRKRCxvREFBZ0c7QUFHaEcsbURBQXNFO0FBV3RFLE1BQU0sR0FBRyxHQUFHLElBQUksc0JBQVMsRUFBRSxDQUFDO0FBRXJCLEtBQUssVUFBVSxPQUFPLENBQUMsS0FBeUI7SUFDckQsTUFBTSxNQUFNLEdBQStCLEVBQUUsaUJBQWlCLEVBQUUsRUFBRSxFQUFFLENBQUM7SUFDckUsSUFBSSxZQUFpQyxDQUFDO0lBQ3RDLElBQUksV0FBdUMsQ0FBQztJQUU1QyxLQUFLLE1BQU0sTUFBTSxJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNuQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQTBCLENBQUM7UUFDL0QsT0FBTyxDQUFDLEdBQUcsQ0FBQztZQUNWLE1BQU0sRUFBRSxpQkFBaUI7WUFDekIsS0FBSztTQUNOLENBQUMsQ0FBQztRQUVILE1BQU0sVUFBVSxHQUFHLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsRUFBRSxjQUFjLEVBQUUsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUM7UUFFN0YsMENBQTBDO1FBQzFDLE1BQU0sU0FBUyxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLHFDQUF3QixDQUFDLEVBQUUsWUFBWSxFQUFFLEtBQUssQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDckcsSUFBSSxTQUFTLENBQUMsTUFBTSxJQUFJLFNBQVMsRUFBRSxDQUFDO1lBQ2xDLG1EQUFtRDtZQUNuRCxPQUFPLENBQUMsR0FBRyxDQUFDO2dCQUNWLE1BQU0sRUFBRSx5QkFBeUI7Z0JBQ2pDLEtB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QztZQUNILFVBQVUsRUFBRSxDQUFDO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRGVzY3JpYmVFeGVjdXRpb25Db21tYW5kLCBTRk5DbGllbnQsIFN0b3BFeGVjdXRpb25Db21tYW5kIH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXNmbic7XG5pbXBvcnQgeyBPY3Rva2l0IH0gZnJvbSAnQG9jdG9raXQvcmVzdCc7XG5pbXBvcnQgKiBhcyBBV1NMYW1iZGEgZnJvbSAnYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBkZWxldGVSdW5uZXIsIGdldE9jdG9raXQsIGdldFJ1bm5lciB9IGZyb20gJy4vbGFtYmRhLWdpdGh1Yic7XG5cbmludGVyZmFjZSBJZGxlUmVhcGVyTGFtYmRhSW5wdXQge1xuICByZWFkb25seSBleGVjdXRpb25Bcm46IHN0cmluZztcbiAgcmVhZG9ubHkgcnVubmVyTmFtZTogc3RyaW5nO1xuICByZWFkb25seSBvd25lcjogc3RyaW5nO1xuICByZWFkb25seSByZXBvOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGluc3RhbGxhdGlvbklkPzogbnVtYmVyO1xuICByZWFkb25seSBtYXhJZGxlU2Vjb25kczogbnVtYmVyO1xufVxuXG5jb25zdCBzZm4gPSBuZXcgU0ZOQ2xpZW50KCk7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuU1FTRXZlbnQpOiBQcm9taXNlPEFXU0xhbWJkYS5TUVNCYXRjaFJlc3BvbnNlPiB7XG4gIGNvbnN0IHJlc3VsdDogQVdTTGFtYmRhLlNRU0JhdGNoUmVzcG9uc2UgPSB7IGJhdGNoSXRlbUZhaWx1cmVzOiBbXSB9O1xuICBsZXQgb2N0b2tpdENhY2hlOiBPY3Rva2l0IHwgdW5kZWZpbmVkO1xuICBsZXQgcnVubmVyTGV2ZWw6ICdyZXBvJyB8ICdvcmcnIHwgdW5kZWZpbmVkO1xuXG4gIGZvciAoY29uc3QgcmVjb3JkIG9mIGV2ZW50LlJlY29yZHMpIHtcbiAgICBjb25zdCBpbnB1dCA9IEpTT04ucGFyc2UocmVjb3JkLmJvZHkpIGFzIElkbGVSZWFwZXJMYW1iZGFJbnB1dDtcbiAgICBjb25zb2xlLmxvZyh7XG4gICAgICBub3RpY2U6ICdDaGVja2luZyBydW5uZXInLFxuICAgICAgaW5wdXQsXG4gICAgfSk7XG5cbiAgICBjb25zdCByZXRyeUxhdGVyID0gKCkgPT4gcmVzdWx0LmJhdGNoSXRlbUZhaWx1cmVzLnB1c2goeyBpdGVtSWRlbnRpZmllcjogcmVjb3JkLm1lc3NhZ2VJZCB9KTtcblxuICAgIC8vIGNoZWNrIGlmIHN0ZXAgZnVuY3Rpb24gaXMgc3RpbGwgcnVubmluZ1xuICAgIGNvbnN0IGV4ZWN1dGlvbiA9IGF3YWl0IHNmbi5zZW5kKG5ldyBEZXNjcmliZUV4ZWN1dGlvbkNvbW1hbmQoeyBleGVjdXRpb25Bcm46IGlucHV0LmV4ZWN1dGlvbkFybiB9KSk7XG4gICAgaWYgKGV4ZWN1dGlvbi5zdGF0dXMgIT0gJ1JVTk5JTkcnKSB7XG4gICAgICAvLyBubyBuZWVkIHRvIHRlc3QgYWdhaW4gYXMgcnVubmVyIGFscmVhZHkgZmluaXNoZWRcbiAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgbm90aWNlOiAnUnVubmVyIGFscmVhZHkgZmluaXNoZWQnLFxuICAgICAgICBpbnB1dCxcbiAgICAgIH0pO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gZ2V0IGdpdGh1YiBhY2Nlc3NcbiAgICBpZiAoIW9jdG9raXRDYWNoZSkge1xuICAgICAgLy8gZ2V0T2N0b2tpdCBjYWxscyBzZWNyZXRzIG1hbmFnZXIgZXZlcnkgdGltZSwgc28gY2FjaGUgdGhlIHJlc3VsdFxuICAgICAgY29uc3QgeyBvY3Rva2l0LCBnaXRodWJTZWNyZXRzIH0gPSBhd2FpdCBnZXRPY3Rva2l0KGlucHV0Lmluc3RhbGxhdGlvbklkKTtcbiAgICAgIC8vIFRPRE8gaWYgaW5zdGFsbGF0aW9uSWQgY2hhbmdlcyBkdXJpbmcgbm9ybWFsIG9wZXJhdGlvbnMsIHdlIG1heSBoYXZlIHNvbWUgcmVjb3JkcyB3aXRoIGdvb2QgaW5zdGFsbGF0aW9uSWQsIGFuZCBzb21lIHdpdGggYmFkXG4gICAgICBvY3Rva2l0Q2FjaGUgPSBvY3Rva2l0O1xuICAgICAgcnVubmVyTGV2ZWwgPSBnaXRodWJTZWNyZXRzLnJ1bm5lckxldmVsO1xuICAgIH1cblxuICAgIC8vIGZpbmQgcnVubmVyXG4gICAgY29uc3QgcnVubmVyID0gYXdhaXQgZ2V0UnVubmVyKG9jdG9raXRDYWNoZSwgcnVubmVyTGV2ZWwsIGlucHV0Lm93bmVyLCBpbnB1dC5yZXBvLCBpbnB1dC5ydW5uZXJOYW1lKTtcbiAgICBpZiAoIXJ1bm5lcikge1xuICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICBub3RpY2U6ICdSdW5uZXIgbm90IHJ1bm5pbmcgeWV0JyxcbiAgICAgICAgaW5wdXQsXG4gICAgICB9KTtcbiAgICAgIHJldHJ5TGF0ZXIoKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIGlmIG5vdCBpZGxlLCB0cnkgYWdhaW4gbGF0ZXJcbiAgICAvLyB3ZSB3YW50IHRvIHRyeSBhZ2FpbiBiZWNhdXNlIHRoZSBydW5uZXIgbWlnaHQgYmUgcmV0cmllZCBkdWUgdG8gZS5nLiBsYW1iZGEgdGltZW91dFxuICAgIC8vIHdlIG5lZWQgdG8ga2VlcCBmb2xsb3dpbmcgdGhlIHJldHJ5IHRvbyBhbmQgbWFrZSBzdXJlIGl0IGRvZXNuJ3QgZ28gaWRsZVxuICAgIGlmIChydW5uZXIuYnVzeSkge1xuICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICBub3RpY2U6ICdSdW5uZXIgaXMgbm90IGlkbGUnLFxuICAgICAgICBpbnB1dCxcbiAgICAgIH0pO1xuICAgICAgcmV0cnlMYXRlcigpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gY2hlY2sgaWYgbWF4IGlkbGUgdGltZW91dCBoYXMgcmVhY2hlZFxuICAgIGxldCBmb3VuZCA9IGZhbHNlO1xuICAgIGZvciAoY29uc3QgbGFiZWwgb2YgcnVubmVyLmxhYmVscykge1xuICAgICAgaWYgKGxhYmVsLm5hbWUudG9Mb3dlckNhc2UoKS5zdGFydHNXaXRoKCdjZGtnaHI6c3RhcnRlZDonKSkge1xuICAgICAgICBjb25zdCBzdGFydGVkID0gcGFyc2VGbG9hdChsYWJlbC5uYW1lLnNwbGl0KCc6JylbMl0pO1xuICAgICAgICBjb25zdCBzdGFydGVkRGF0ZSA9IG5ldyBEYXRlKHN0YXJ0ZWQgKiAxMDAwKTtcbiAgICAgICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcbiAgICAgICAgY29uc3QgZGlmZk1zID0gbm93LmdldFRpbWUoKSAtIHN0YXJ0ZWREYXRlLmdldFRpbWUoKTtcblxuICAgICAgICBjb25zb2xlLmxvZyh7XG4gICAgICAgICAgbm90aWNlOiBgUnVubmVyICR7aW5wdXQucnVubmVyTmFtZX0gc3RhcnRlZCAke2RpZmZNcyAvIDEwMDB9IHNlY29uZHMgYWdvYCxcbiAgICAgICAgICBpbnB1dCxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKGRpZmZNcyA+IDEwMDAgKiBpbnB1dC5tYXhJZGxlU2Vjb25kcykge1xuICAgICAgICAgIC8vIG1heCBpZGxlIHRpbWUgcmVhY2hlZCwgZGVsZXRlIHJ1bm5lclxuICAgICAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgICAgIG5vdGljZTogYFJ1bm5lciAke2lucHV0LnJ1bm5lck5hbWV9IGlzIGlkbGUgZm9yIHRvbyBsb25nYCxcbiAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIHN0b3Agc3RlcCBmdW5jdGlvbiBmaXJzdCwgc28gaXQncyBtYXJrZWQgYXMgYWJvcnRlZCB3aXRoIHRoZSBwcm9wZXIgZXJyb3JcbiAgICAgICAgICAgIC8vIGlmIHdlIGRlbGV0ZSB0aGUgcnVubmVyIGZpcnN0LCB0aGUgc3RlcCBmdW5jdGlvbiB3aWxsIGJlIG1hcmtlZCBhcyBmYWlsZWQgd2l0aCBhIGdlbmVyaWMgZXJyb3JcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgICAgICAgbm90aWNlOiBgU3RvcHBpbmcgc3RlcCBmdW5jdGlvbiAke2lucHV0LmV4ZWN1dGlvbkFybn0uLi5gLFxuICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgYXdhaXQgc2ZuLnNlbmQobmV3IFN0b3BFeGVjdXRpb25Db21tYW5kKHtcbiAgICAgICAgICAgICAgZXhlY3V0aW9uQXJuOiBpbnB1dC5leGVjdXRpb25Bcm4sXG4gICAgICAgICAgICAgIGVycm9yOiAnSWRsZVJ1bm5lcicsXG4gICAgICAgICAgICAgIGNhdXNlOiBgUnVubmVyICR7aW5wdXQucnVubmVyTmFtZX0gb24gJHtpbnB1dC5vd25lcn0vJHtpbnB1dC5yZXBvfSBpcyBpZGxlIGZvciB0b28gbG9uZyAoJHtkaWZmTXMgLyAxMDAwfSBzZWNvbmRzIGFuZCBsaW1pdCBpcyAke2lucHV0Lm1heElkbGVTZWNvbmRzfSBzZWNvbmRzKWAsXG4gICAgICAgICAgICB9KSk7XG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcih7XG4gICAgICAgICAgICAgIG5vdGljZTogYEZhaWxlZCB0byBzdG9wIHN0ZXAgZnVuY3Rpb24gJHtpbnB1dC5leGVjdXRpb25Bcm59OiAke2V9YCxcbiAgICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHJ5TGF0ZXIoKTtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyh7XG4gICAgICAgICAgICAgIG5vdGljZTogYERlbGV0aW5nIHJ1bm5lciAke3J1bm5lci5pZH0uLi5gLFxuICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgYXdhaXQgZGVsZXRlUnVubmVyKG9jdG9raXRDYWNoZSwgcnVubmVyTGV2ZWwsIGlucHV0Lm93bmVyLCBpbnB1dC5yZXBvLCBydW5uZXIuaWQpO1xuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3Ioe1xuICAgICAgICAgICAgICBub3RpY2U6IGBGYWlsZWQgdG8gZGVsZXRlIHJ1bm5lciAke3J1bm5lci5pZH06ICR7ZX1gLFxuICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0cnlMYXRlcigpO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIHN0aWxsIGlkbGUsIHRpbWVvdXQgbm90IHJlYWNoZWQgLS0gcmV0cnkgbGF0ZXJcbiAgICAgICAgICByZXRyeUxhdGVyKCk7XG4gICAgICAgIH1cblxuICAgICAgICBmb3VuZCA9IHRydWU7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmICghZm91bmQpIHtcbiAgICAgIC8vIG5vIHN0YXJ0ZWQgbGFiZWw/IHJldHJ5IGxhdGVyIChpdCB3b24ndCByZXRyeSBmb3JldmVyIGFzIGV2ZW50dWFsbHkgdGhlIHJ1bm5lciB3aWxsIHN0b3AgYW5kIHRoZSBzdGVwIGZ1bmN0aW9uIHdpbGwgZmluaXNoKVxuICAgICAgY29uc29sZS5lcnJvcih7XG4gICAgICAgIG5vdGljZTogJ05vIGBjZGtnaHI6c3RhcnRlZDp4eHhgIGxhYmVsIGZvdW5kPz8/JyxcbiAgICAgICAgaW5wdXQsXG4gICAgICB9KTtcbiAgICAgIHJldHJ5TGF0ZXIoKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmVzdWx0O1xufVxuIl19