From fc130f48f08cbc3ec9abb8c297370617a2a0c08a Mon Sep 17 00:00:00 2001 From: BrusR Date: Fri, 6 Dec 2024 11:53:03 -0500 Subject: [PATCH] Added support to wait for instance not busy (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * extended github action by adding multiple labels and multiple self-hosted ec2 capability * test 1 of new action * added new input * solved comments * added support to wait for instance not busy * moved the waiting for not busy * added logging to debug code * index.js synthetized * Update gh * Update dist * Update runnersNotBusy * Update AreRunnersOnline * update index * . * . * . * . * Add hash (#3) * Add hash * test16 * update dist * release 18 * update substr * update dist * fix lint error --------- Co-authored-by: marcosmicJack Co-authored-by: Marcos Alarcón <78625129+marcosmicJack@users.noreply.github.com> --- dist/index.js | 97 ++++++++++++++++++++++++++++++++++++++------------- src/config.js | 11 ++++-- src/gh.js | 86 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 146 insertions(+), 48 deletions(-) diff --git a/dist/index.js b/dist/index.js index 0f70a7f..95d896c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -62932,14 +62932,19 @@ const github = __webpack_require__(95438); class Config { constructor() { + let label_list = JSON.parse(core.getInput('labels')); + let mode_option = core.getInput('mode') + if (mode_option === 'start'){ + label_list.push(this.generateUniqueLabel()) + } this.input = { - mode: core.getInput('mode'), + mode: mode_option, githubToken: core.getInput('github-token'), ec2ImageId: core.getInput('ec2-image-id'), ec2InstanceType: core.getInput('ec2-instance-type'), subnetId: core.getInput('subnet-id'), securityGroupId: core.getInput('security-group-id'), - labels: core.getInput('labels'), + labels: label_list, ec2InstanceIds: core.getInput('ec2-instance-ids'), iamRoleName: core.getInput('iam-role-name'), runnerHomeDir: core.getInput('runner-home-dir'), @@ -62990,7 +62995,7 @@ class Config { return Math.random().toString(36).substr(2, 5); } getLabels() { - return JSON.parse(this.input.labels) + return this.input.labels } getEc2InstanceIds(){ return JSON.parse(this.input.ec2InstanceIds) @@ -63014,8 +63019,6 @@ const core = __webpack_require__(42186); const github = __webpack_require__(95438); const _ = __webpack_require__(90250); const config = __webpack_require__(34570); - - function separateArrayWithCommas(arr) { if (!Array.isArray(arr)) { return "Input is not an array"; @@ -63024,27 +63027,45 @@ function separateArrayWithCommas(arr) { return arr.join(","); } -function areRunnersOnline(runners){ +function areRunnersOnline(runners) { core.info("Here at areRunnersOnline") core.info(runners) - if(runners){ + if (runners){ core.info("Critical logger") - var result = true runners.forEach((runner) => { - if (runner.status !== 'online'){ - result = false + if (runner.status !== 'online'){ + core.info(`GitHub self-hosted runner number ${runner.name}, is not online yet`); + return false } }); - return result + return true } else { return false } } + +function areRunnersNotBusy(runners) { + core.info("Here at areRunnersNotBusy") + core.info(runners) + if (runners){ + core.info(`Checking if runners are not busy`); + runners.forEach((runner) => { + if (runner.busy){ + core.info(`GitHub self-hosted runner number ${runner.name}, is still busy`); + return false + } + }); + core.info(`All runners are not busy`); + return true + } else { + core.info(`There are no more runners`); + return false + } +} // use the unique label to find the runner // as we don't have the runner's id, it's not possible to get it in any other way async function getRunners(multiple_labels) { const octokit = github.getOctokit(config.input.githubToken); - try { var foundRunners = await octokit.paginate('GET /repos/{owner}/{repo}/actions/runners', config.githubContext); multiple_labels.forEach((label) => { @@ -63056,11 +63077,9 @@ async function getRunners(multiple_labels) { return null; } } - // get GitHub Registration Token for registering a self-hosted runner async function getRegistrationToken() { const octokit = github.getOctokit(config.input.githubToken); - try { const response = await octokit.request('POST /repos/{owner}/{repo}/actions/runners/registration-token', config.githubContext); core.info('GitHub Registration Token is received'); @@ -63070,16 +63089,51 @@ async function getRegistrationToken() { throw error; } } + +async function waitForRunnersNotBusy(labels) { + const timeoutMinutes = 5; + const retryIntervalSeconds = 10; + const quietPeriodSeconds = 30; + let waitSeconds = 0; + + core.info(`Waiting ${quietPeriodSeconds}s for the AWS EC2 instances be not busy in GitHut`); + await new Promise(r => setTimeout(r, quietPeriodSeconds * 1000)); + core.info(`Checking every ${retryIntervalSeconds}s if the GitHub self-hosted runners are not busy`); + + return new Promise((resolve, reject) => { + const interval = setInterval(async () => { + const runners = await getRunners(labels); + + if (waitSeconds > timeoutMinutes * 60) { + core.error('GitHub self-hosted runners failed to be not busy -> TIMEOUT'); + clearInterval(interval); + reject(`A timeout of ${timeoutMinutes} minutes is exceeded. Your AWS EC2 instances were not able to be not busy.`); + } + + if (areRunnersNotBusy(runners)) { + runners.forEach((runner, index ) => { + core.info(`GitHub self-hosted runner number ${index}, ${runner.name}, is not busy`); + }); + clearInterval(interval); + resolve(); + } else { + waitSeconds += retryIntervalSeconds; + core.info('Checking...'); + } + }, retryIntervalSeconds * 1000); + }); +} + async function removeRunners() { const runners = await getRunners(config.getLabels()); const octokit = github.getOctokit(config.input.githubToken); - // skip the runner removal process if no runners are found if (!runners || runners.length === 0) { core.info(`No GitHub self-hosted runners with labels ${separateArrayWithCommas(config.getLabels())} found, so the removal is skipped`); return; } - + // Wait until all runner are not in busy state + await waitForRunnersNotBusy(config.getLabels()) // Use Promise.all to remove runners asynchronously const removalPromises = runners.map(async (runner) => { try { @@ -63090,7 +63144,6 @@ async function removeRunners() { throw error; } }); - try { await Promise.all(removalPromises); core.info('All GitHub self-hosted runners are removed'); @@ -63099,30 +63152,25 @@ async function removeRunners() { throw error; } } - async function waitForRunnersRegistered(labels) { const timeoutMinutes = 5; const retryIntervalSeconds = 10; const quietPeriodSeconds = 30; let waitSeconds = 0; - core.info(`Waiting ${quietPeriodSeconds}s for the AWS EC2 instances to be registered in GitHub as a new self-hosted runner`); await new Promise(r => setTimeout(r, quietPeriodSeconds * 1000)); core.info(`Checking every ${retryIntervalSeconds}s if the GitHub self-hosted runner is registered`); - return new Promise((resolve, reject) => { const interval = setInterval(async () => { + core.info(`Labels: ${labels}`) const runners = await getRunners(labels); - core.info("After getRunners") - core.info(runners) - if (waitSeconds > timeoutMinutes * 60) { core.error('GitHub self-hosted runner registration error'); clearInterval(interval); reject(`A timeout of ${timeoutMinutes} minutes is exceeded. Your AWS EC2 instance was not able to register itself in GitHub as a new self-hosted runner.`); } - if (areRunnersOnline(runners)) { + core.info("Are Runners Online") runners.forEach((runner, index ) => { core.info(`GitHub self-hosted runner number ${index}, ${runner.name}, is registered and ready to use`); }); @@ -63138,6 +63186,7 @@ async function waitForRunnersRegistered(labels) { module.exports = { getRegistrationToken, + waitForRunnersNotBusy, removeRunners, waitForRunnersRegistered, }; diff --git a/src/config.js b/src/config.js index 9fdcf0b..c462306 100644 --- a/src/config.js +++ b/src/config.js @@ -3,14 +3,19 @@ const github = require('@actions/github'); class Config { constructor() { + const label_list = JSON.parse(core.getInput('labels')); + const mode_option = core.getInput('mode') + if (mode_option === 'start'){ + label_list.push(this.generateUniqueLabel()) + } this.input = { - mode: core.getInput('mode'), + mode: mode_option, githubToken: core.getInput('github-token'), ec2ImageId: core.getInput('ec2-image-id'), ec2InstanceType: core.getInput('ec2-instance-type'), subnetId: core.getInput('subnet-id'), securityGroupId: core.getInput('security-group-id'), - labels: core.getInput('labels'), + labels: label_list, ec2InstanceIds: core.getInput('ec2-instance-ids'), iamRoleName: core.getInput('iam-role-name'), runnerHomeDir: core.getInput('runner-home-dir'), @@ -61,7 +66,7 @@ class Config { return Math.random().toString(36).substr(2, 5); } getLabels() { - return JSON.parse(this.input.labels) + return this.input.labels } getEc2InstanceIds(){ return JSON.parse(this.input.ec2InstanceIds) diff --git a/src/gh.js b/src/gh.js index 74c8f12..6f5bc40 100644 --- a/src/gh.js +++ b/src/gh.js @@ -2,8 +2,6 @@ const core = require('@actions/core'); const github = require('@actions/github'); const _ = require('lodash'); const config = require('./config'); - - function separateArrayWithCommas(arr) { if (!Array.isArray(arr)) { return "Input is not an array"; @@ -12,27 +10,45 @@ function separateArrayWithCommas(arr) { return arr.join(","); } -function areRunnersOnline(runners){ +function areRunnersOnline(runners) { core.info("Here at areRunnersOnline") core.info(runners) - if(runners){ + if (runners){ core.info("Critical logger") - var result = true runners.forEach((runner) => { - if (runner.status !== 'online'){ - result = false + if (runner.status !== 'online'){ + core.info(`GitHub self-hosted runner number ${runner.name}, is not online yet`); + return false } }); - return result + return true } else { return false } } + +function areRunnersNotBusy(runners) { + core.info("Here at areRunnersNotBusy") + core.info(runners) + if (runners){ + core.info(`Checking if runners are not busy`); + runners.forEach((runner) => { + if (runner.busy){ + core.info(`GitHub self-hosted runner number ${runner.name}, is still busy`); + return false + } + }); + core.info(`All runners are not busy`); + return true + } else { + core.info(`There are no more runners`); + return false + } +} // use the unique label to find the runner // as we don't have the runner's id, it's not possible to get it in any other way async function getRunners(multiple_labels) { const octokit = github.getOctokit(config.input.githubToken); - try { var foundRunners = await octokit.paginate('GET /repos/{owner}/{repo}/actions/runners', config.githubContext); multiple_labels.forEach((label) => { @@ -44,11 +60,9 @@ async function getRunners(multiple_labels) { return null; } } - // get GitHub Registration Token for registering a self-hosted runner async function getRegistrationToken() { const octokit = github.getOctokit(config.input.githubToken); - try { const response = await octokit.request('POST /repos/{owner}/{repo}/actions/runners/registration-token', config.githubContext); core.info('GitHub Registration Token is received'); @@ -58,16 +72,51 @@ async function getRegistrationToken() { throw error; } } + +async function waitForRunnersNotBusy(labels) { + const timeoutMinutes = 5; + const retryIntervalSeconds = 10; + const quietPeriodSeconds = 30; + let waitSeconds = 0; + + core.info(`Waiting ${quietPeriodSeconds}s for the AWS EC2 instances be not busy in GitHut`); + await new Promise(r => setTimeout(r, quietPeriodSeconds * 1000)); + core.info(`Checking every ${retryIntervalSeconds}s if the GitHub self-hosted runners are not busy`); + + return new Promise((resolve, reject) => { + const interval = setInterval(async () => { + const runners = await getRunners(labels); + + if (waitSeconds > timeoutMinutes * 60) { + core.error('GitHub self-hosted runners failed to be not busy -> TIMEOUT'); + clearInterval(interval); + reject(`A timeout of ${timeoutMinutes} minutes is exceeded. Your AWS EC2 instances were not able to be not busy.`); + } + + if (areRunnersNotBusy(runners)) { + runners.forEach((runner, index ) => { + core.info(`GitHub self-hosted runner number ${index}, ${runner.name}, is not busy`); + }); + clearInterval(interval); + resolve(); + } else { + waitSeconds += retryIntervalSeconds; + core.info('Checking...'); + } + }, retryIntervalSeconds * 1000); + }); +} + async function removeRunners() { const runners = await getRunners(config.getLabels()); const octokit = github.getOctokit(config.input.githubToken); - // skip the runner removal process if no runners are found if (!runners || runners.length === 0) { core.info(`No GitHub self-hosted runners with labels ${separateArrayWithCommas(config.getLabels())} found, so the removal is skipped`); return; } - + // Wait until all runner are not in busy state + await waitForRunnersNotBusy(config.getLabels()) // Use Promise.all to remove runners asynchronously const removalPromises = runners.map(async (runner) => { try { @@ -78,7 +127,6 @@ async function removeRunners() { throw error; } }); - try { await Promise.all(removalPromises); core.info('All GitHub self-hosted runners are removed'); @@ -87,30 +135,25 @@ async function removeRunners() { throw error; } } - async function waitForRunnersRegistered(labels) { const timeoutMinutes = 5; const retryIntervalSeconds = 10; const quietPeriodSeconds = 30; let waitSeconds = 0; - core.info(`Waiting ${quietPeriodSeconds}s for the AWS EC2 instances to be registered in GitHub as a new self-hosted runner`); await new Promise(r => setTimeout(r, quietPeriodSeconds * 1000)); core.info(`Checking every ${retryIntervalSeconds}s if the GitHub self-hosted runner is registered`); - return new Promise((resolve, reject) => { const interval = setInterval(async () => { + core.info(`Labels: ${labels}`) const runners = await getRunners(labels); - core.info("After getRunners") - core.info(runners) - if (waitSeconds > timeoutMinutes * 60) { core.error('GitHub self-hosted runner registration error'); clearInterval(interval); reject(`A timeout of ${timeoutMinutes} minutes is exceeded. Your AWS EC2 instance was not able to register itself in GitHub as a new self-hosted runner.`); } - if (areRunnersOnline(runners)) { + core.info("Are Runners Online") runners.forEach((runner, index ) => { core.info(`GitHub self-hosted runner number ${index}, ${runner.name}, is registered and ready to use`); }); @@ -126,6 +169,7 @@ async function waitForRunnersRegistered(labels) { module.exports = { getRegistrationToken, + waitForRunnersNotBusy, removeRunners, waitForRunnersRegistered, };