Skip to content

Commit 133757e

Browse files
committed
fix: OIDC Parallel Requests error
1 parent 9b99800 commit 133757e

File tree

2 files changed

+66
-13
lines changed

2 files changed

+66
-13
lines changed

index.js

+43-12
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,29 @@ function getStsClient(region) {
236236
});
237237
}
238238

239+
let defaultSleep = function (ms) {
240+
return new Promise((resolve) => setTimeout(resolve, ms));
241+
};
242+
let sleep = defaultSleep;
243+
244+
// retryAndBackoff retries with exponential backoff the promise if the error isRetryable upto maxRetries time.
245+
const retryAndBackoff = async (fn, isRetryable, retries = 0, maxRetries = 12, base = 50) => {
246+
try {
247+
return await fn();
248+
} catch (err) {
249+
if (!isRetryable) {
250+
throw err;
251+
}
252+
// It's retryable, so sleep and retry.
253+
await sleep(Math.random() * (Math.pow(2, retries) * base) );
254+
retries += 1;
255+
if (retries === maxRetries) {
256+
throw err;
257+
}
258+
return await retryAndBackoff(fn, isRetryable, retries, maxRetries, base);
259+
}
260+
}
261+
239262
async function run() {
240263
try {
241264
// Get inputs
@@ -303,17 +326,18 @@ async function run() {
303326

304327
// Get role credentials if configured to do so
305328
if (roleToAssume) {
306-
const roleCredentials = await assumeRole({
307-
sourceAccountId,
308-
region,
309-
roleToAssume,
310-
roleExternalId,
311-
roleDurationSeconds,
312-
roleSessionName,
313-
roleSkipSessionTagging,
314-
webIdentityTokenFile,
315-
webIdentityToken
316-
});
329+
const roleCredentials = await retryAndBackoff(
330+
async () => { return await assumeRole({
331+
sourceAccountId,
332+
region,
333+
roleToAssume,
334+
roleExternalId,
335+
roleDurationSeconds,
336+
roleSessionName,
337+
roleSkipSessionTagging,
338+
webIdentityTokenFile,
339+
webIdentityToken
340+
}) }, true);
317341
exportCredentials(roleCredentials);
318342
// We need to validate the credentials in 2 of our use-cases
319343
// First: self-hosted runners. If the GITHUB_ACTIONS environment variable
@@ -337,7 +361,14 @@ async function run() {
337361
}
338362
}
339363

340-
module.exports = run;
364+
exports.withSleep = function (s) {
365+
sleep = s;
366+
};
367+
exports.reset = function () {
368+
sleep = defaultSleep;
369+
};
370+
371+
exports.run = run
341372

342373
/* istanbul ignore next */
343374
if (require.main === module) {

index.test.js

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const core = require('@actions/core');
22
const assert = require('assert');
33
const aws = require('aws-sdk');
4-
const run = require('./index.js');
4+
const { run, withSleep, reset } = require('./index.js');
55

66
jest.mock('@actions/core');
77

@@ -156,10 +156,15 @@ describe('Configure AWS Credentials', () => {
156156
}
157157
}
158158
});
159+
160+
withSleep(() => {
161+
return Promise.resolve();
162+
});
159163
});
160164

161165
afterEach(() => {
162166
process.env = OLD_ENV;
167+
reset();
163168
});
164169

165170
test('exports env vars', async () => {
@@ -612,6 +617,23 @@ describe('Configure AWS Credentials', () => {
612617
expect(core.setSecret).toHaveBeenNthCalledWith(3, FAKE_STS_SESSION_TOKEN);
613618
});
614619

620+
test('role assumption fails after maximun trials using OIDC Provider', async () => {
621+
process.env.GITHUB_ACTIONS = 'true';
622+
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token';
623+
624+
core.getInput = jest
625+
.fn()
626+
.mockImplementation(mockGetInput({'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION}));
627+
628+
mockStsAssumeRoleWithWebIdentity.mockReset();
629+
mockStsAssumeRoleWithWebIdentity.mockImplementation(() => {
630+
throw new Error();
631+
});
632+
633+
await assert.rejects(() => run());
634+
expect(mockStsAssumeRoleWithWebIdentity).toHaveBeenCalledTimes(12)
635+
});
636+
615637
test('role external ID provided', async () => {
616638
core.getInput = jest
617639
.fn()

0 commit comments

Comments
 (0)