Skip to content

Commit e5fb8eb

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/lambdas/aws-powertools-d1957b442c
2 parents 38b2807 + 95aa6a2 commit e5fb8eb

File tree

11 files changed

+177
-38
lines changed

11 files changed

+177
-38
lines changed

.github/workflows/actions.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
path: results.sarif
5252

5353
- name: Upload SARIF file
54-
uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
54+
uses: github/codeql-action/upload-sarif@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.5
5555
with:
5656
sarif_file: results.sarif
5757
category: actions-zizmor

.github/workflows/codeql.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ jobs:
3939

4040
# Initializes the CodeQL tools for scanning.
4141
- name: Initialize CodeQL
42-
uses: github/codeql-action/init@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
42+
uses: github/codeql-action/init@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.5
4343
with:
4444
languages: ${{ matrix.language }}
4545
build-mode: none
4646

4747
- name: Perform CodeQL Analysis
48-
uses: github/codeql-action/analyze@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # v3.29.5
48+
uses: github/codeql-action/analyze@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.5
4949
with:
5050
category: "/language:${{matrix.language}}"

.github/workflows/dependency-review.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ jobs:
2828
with:
2929
persist-credentials: false
3030
- name: 'Dependency Review'
31-
uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1
31+
uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 # v4.7.2
3232
with:
3333
comment-summary-in-pr: always
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
mkdocs-material==9.6.16
1+
mkdocs-material==9.6.17

.github/workflows/mkdocs/requirements.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ charset-normalizer==3.4.2 \
118118
click==8.2.1 \
119119
--hash=sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202 \
120120
--hash=sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b
121-
# via mkdocs
121+
# via
122+
# mkdocs
123+
# mkdocs-material
122124
colorama==0.4.6 \
123125
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
124126
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
@@ -223,9 +225,9 @@ mkdocs-get-deps==0.2.0 \
223225
--hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \
224226
--hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
225227
# via mkdocs
226-
mkdocs-material==9.6.16 \
227-
--hash=sha256:8d1a1282b892fe1fdf77bfeb08c485ba3909dd743c9ba69a19a40f637c6ec18c \
228-
--hash=sha256:d07011df4a5c02ee0877496d9f1bfc986cfb93d964799b032dd99fe34c0e9d19
228+
mkdocs-material==9.6.17 \
229+
--hash=sha256:221dd8b37a63f52e580bcab4a7e0290e4a6f59bd66190be9c3d40767e05f9417 \
230+
--hash=sha256:48ae7aec72a3f9f501a70be3fbd329c96ff5f5a385b67a1563e5ed5ce064affe
229231
# via -r requirements.in
230232
mkdocs-material-extensions==1.3.1 \
231233
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \

.github/workflows/ossf-scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ jobs:
4848
# Upload the results to GitHub's code scanning dashboard (optional).
4949
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
5050
- name: "Upload to code-scanning"
51-
uses: github/codeql-action/upload-sarif@76621b61decf072c1cee8dd1ce2d2a82d33c17ed
51+
uses: github/codeql-action/upload-sarif@96f518a34f7a870018057716cc4d7a5c014bd61c
5252
with:
5353
sarif_file: results.sarif

.github/workflows/semantic-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2222
with:
2323
persist-credentials: false
24-
- uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
24+
- uses: amannn/action-semantic-pull-request@fdd4d3ddf614fbcd8c29e4b106d3bbe0cb2c605d # v6.0.1
2525
name: Check PR for Semantic Commit Message
2626
env:
2727
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/terraform.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
key: tflint-${{ hashFiles('.tflint.hcl') }}
6161
- if: contains(matrix.terraform, '1.5.')
6262
name: Setup TFLint
63-
uses: terraform-linters/setup-tflint@90f302c255ef959cbfb4bd10581afecdb7ece3e6 # v4.1.1
63+
uses: terraform-linters/setup-tflint@ae78205cfffec9e8d93fd2b3115c7e9d3166d4b6 # v5.0.0
6464
with:
6565
github_token: ${{ secrets.GITHUB_TOKEN }}
6666
- if: contains(matrix.terraform, '1.5.')
@@ -126,7 +126,7 @@ jobs:
126126
key: tflint-${{ hashFiles('.tflint.hcl') }}
127127
- if: contains(matrix.terraform, '1.3.')
128128
name: Setup TFLint
129-
uses: terraform-linters/setup-tflint@90f302c255ef959cbfb4bd10581afecdb7ece3e6 # v4.1.1
129+
uses: terraform-linters/setup-tflint@ae78205cfffec9e8d93fd2b3115c7e9d3166d4b6 # v5.0.0
130130
with:
131131
github_token: ${{ secrets.GITHUB_TOKEN }}
132132
- if: contains(matrix.terraform, '1.3.')
@@ -189,7 +189,7 @@ jobs:
189189
key: tflint-${{ hashFiles('.tflint.hcl') }}
190190
- if: contains(matrix.terraform, '1.5.')
191191
name: Setup TFLint
192-
uses: terraform-linters/setup-tflint@90f302c255ef959cbfb4bd10581afecdb7ece3e6 # v4.1.1
192+
uses: terraform-linters/setup-tflint@ae78205cfffec9e8d93fd2b3115c7e9d3166d4b6 # v5.0.0
193193
with:
194194
github_token: ${{ secrets.GITHUB_TOKEN }}
195195
- if: contains(matrix.terraform, '1.5.')

lambdas/functions/control-plane/src/scale-runners/scale-down.test.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Octokit } from '@octokit/rest';
2+
import { RequestError } from '@octokit/request-error';
23
import moment from 'moment';
34
import nock from 'nock';
45

@@ -403,6 +404,121 @@ describe('Scale down runners', () => {
403404
expect(mockTerminateRunners).toHaveBeenCalledWith(orphanRunner.instanceId);
404405
});
405406

407+
it('Should handle 404 error when checking orphaned runner (JIT) - treat as orphaned', async () => {
408+
// arrange
409+
const orphanRunner = createRunnerTestData(
410+
'orphan-jit-404',
411+
type,
412+
MINIMUM_BOOT_TIME + 1,
413+
false,
414+
true,
415+
true, // should be terminated when 404
416+
undefined,
417+
1234567890,
418+
);
419+
const runners = [orphanRunner];
420+
421+
mockGitHubRunners([]);
422+
mockAwsRunners(runners);
423+
424+
// Mock 404 error response
425+
const error404 = new RequestError('Runner not found', 404, {
426+
request: {
427+
method: 'GET',
428+
url: 'https://api.github.com/test',
429+
headers: {},
430+
},
431+
});
432+
433+
if (type === 'Repo') {
434+
mockOctokit.actions.getSelfHostedRunnerForRepo.mockRejectedValueOnce(error404);
435+
} else {
436+
mockOctokit.actions.getSelfHostedRunnerForOrg.mockRejectedValueOnce(error404);
437+
}
438+
439+
// act
440+
await scaleDown();
441+
442+
// assert - should terminate since 404 means runner doesn't exist on GitHub
443+
expect(mockTerminateRunners).toHaveBeenCalledWith(orphanRunner.instanceId);
444+
});
445+
446+
it('Should handle 404 error when checking runner busy state - treat as not busy', async () => {
447+
// arrange
448+
const runner = createRunnerTestData(
449+
'runner-404',
450+
type,
451+
MINIMUM_TIME_RUNNING_IN_MINUTES + 1,
452+
true,
453+
false,
454+
true, // should be terminated since not busy due to 404
455+
);
456+
const runners = [runner];
457+
458+
mockGitHubRunners(runners);
459+
mockAwsRunners(runners);
460+
461+
// Mock 404 error response for busy state check
462+
const error404 = new RequestError('Runner not found', 404, {
463+
request: {
464+
method: 'GET',
465+
url: 'https://api.github.com/test',
466+
headers: {},
467+
},
468+
});
469+
470+
if (type === 'Repo') {
471+
mockOctokit.actions.getSelfHostedRunnerForRepo.mockRejectedValueOnce(error404);
472+
} else {
473+
mockOctokit.actions.getSelfHostedRunnerForOrg.mockRejectedValueOnce(error404);
474+
}
475+
476+
// act
477+
await scaleDown();
478+
479+
// assert - should terminate since 404 means runner is not busy
480+
checkTerminated(runners);
481+
});
482+
483+
it('Should re-throw non-404 errors when checking runner state', async () => {
484+
// arrange
485+
const orphanRunner = createRunnerTestData(
486+
'orphan-error',
487+
type,
488+
MINIMUM_BOOT_TIME + 1,
489+
false,
490+
true,
491+
false,
492+
undefined,
493+
1234567890,
494+
);
495+
const runners = [orphanRunner];
496+
497+
mockGitHubRunners([]);
498+
mockAwsRunners(runners);
499+
500+
// Mock non-404 error response
501+
const error500 = new RequestError('Internal server error', 500, {
502+
request: {
503+
method: 'GET',
504+
url: 'https://api.github.com/test',
505+
headers: {},
506+
},
507+
});
508+
509+
if (type === 'Repo') {
510+
mockOctokit.actions.getSelfHostedRunnerForRepo.mockRejectedValueOnce(error500);
511+
} else {
512+
mockOctokit.actions.getSelfHostedRunnerForOrg.mockRejectedValueOnce(error500);
513+
}
514+
515+
// act & assert - should not throw because error handling is in terminateOrphan
516+
await expect(scaleDown()).resolves.not.toThrow();
517+
518+
// Should not terminate since the error was not a 404
519+
expect(terminateRunner).not.toHaveBeenCalledWith(orphanRunner.instanceId);
520+
});
521+
406522
it(`Should ignore errors when termination orphan fails.`, async () => {
407523
// setup
408524
const orphanRunner = createRunnerTestData('orphan-1', type, MINIMUM_BOOT_TIME + 1, false, true, true);

lambdas/functions/control-plane/src/scale-runners/scale-down.ts

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Octokit } from '@octokit/rest';
22
import { Endpoints } from '@octokit/types';
3+
import { RequestError } from '@octokit/request-error';
34
import { createChildLogger } from '@aws-github-runner/aws-powertools-util';
45
import moment from 'moment';
56

@@ -55,25 +56,39 @@ async function getGitHubSelfHostedRunnerState(
5556
client: Octokit,
5657
ec2runner: RunnerInfo,
5758
runnerId: number,
58-
): Promise<RunnerState> {
59-
const state =
60-
ec2runner.type === 'Org'
61-
? await client.actions.getSelfHostedRunnerForOrg({
62-
runner_id: runnerId,
63-
org: ec2runner.owner,
64-
})
65-
: await client.actions.getSelfHostedRunnerForRepo({
66-
runner_id: runnerId,
67-
owner: ec2runner.owner.split('/')[0],
68-
repo: ec2runner.owner.split('/')[1],
69-
});
70-
metricGitHubAppRateLimit(state.headers);
71-
72-
return state.data;
59+
): Promise<RunnerState | null> {
60+
try {
61+
const state =
62+
ec2runner.type === 'Org'
63+
? await client.actions.getSelfHostedRunnerForOrg({
64+
runner_id: runnerId,
65+
org: ec2runner.owner,
66+
})
67+
: await client.actions.getSelfHostedRunnerForRepo({
68+
runner_id: runnerId,
69+
owner: ec2runner.owner.split('/')[0],
70+
repo: ec2runner.owner.split('/')[1],
71+
});
72+
metricGitHubAppRateLimit(state.headers);
73+
74+
return state.data;
75+
} catch (error) {
76+
if (error instanceof RequestError && error.status === 404) {
77+
logger.info(`Runner '${ec2runner.instanceId}' with GitHub Runner ID '${runnerId}' not found on GitHub (404)`);
78+
return null;
79+
}
80+
throw error;
81+
}
7382
}
7483

7584
async function getGitHubRunnerBusyState(client: Octokit, ec2runner: RunnerInfo, runnerId: number): Promise<boolean> {
7685
const state = await getGitHubSelfHostedRunnerState(client, ec2runner, runnerId);
86+
if (state === null) {
87+
logger.info(
88+
`Runner '${ec2runner.instanceId}' - GitHub Runner ID '${runnerId}' - Not found on GitHub, treating as not busy`,
89+
);
90+
return false;
91+
}
7792
logger.info(`Runner '${ec2runner.instanceId}' - GitHub Runner ID '${runnerId}' - Busy: ${state.busy}`);
7893
return state.busy;
7994
}
@@ -227,12 +242,18 @@ async function lastChanceCheckOrphanRunner(runner: RunnerList): Promise<boolean>
227242
const ec2Instance = runner as RunnerInfo;
228243
const state = await getGitHubSelfHostedRunnerState(client, ec2Instance, runnerId);
229244
let isOrphan = false;
230-
logger.debug(
231-
`Runner '${runner.instanceId}' is '${state.status}' and is currently '${state.busy ? 'busy' : 'idle'}'.`,
232-
);
233-
const isOfflineAndBusy = state.status === 'offline' && state.busy;
234-
if (isOfflineAndBusy) {
245+
246+
if (state === null) {
247+
logger.debug(`Runner '${runner.instanceId}' not found on GitHub, treating as orphaned.`);
235248
isOrphan = true;
249+
} else {
250+
logger.debug(
251+
`Runner '${runner.instanceId}' is '${state.status}' and is currently '${state.busy ? 'busy' : 'idle'}'.`,
252+
);
253+
const isOfflineAndBusy = state.status === 'offline' && state.busy;
254+
if (isOfflineAndBusy) {
255+
isOrphan = true;
256+
}
236257
}
237258
logger.info(`Runner '${runner.instanceId}' is judged to ${isOrphan ? 'be' : 'not be'} orphaned.`);
238259
return isOrphan;

0 commit comments

Comments
 (0)