mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-03-12 18:07:10 -04:00
feat: Add global timeout support (#1487)
This commit is contained in:
@@ -153,6 +153,7 @@ See [action.yml](./action.yml) for more detail.
|
|||||||
| use-existing-credentials | When set, the action will check if existing credentials are valid and exit if they are. Defaults to false. | No |
|
| use-existing-credentials | When set, the action will check if existing credentials are valid and exit if they are. Defaults to false. | No |
|
||||||
| allowed-account-ids | A comma-delimited list of expected AWS account IDs. The action will fail if we receive credentials for the wrong account. | No |
|
| allowed-account-ids | A comma-delimited list of expected AWS account IDs. The action will fail if we receive credentials for the wrong account. | No |
|
||||||
| force-skip-oidc | When set, the action will skip using GitHub OIDC provider even if the id-token permission is set. | No |
|
| force-skip-oidc | When set, the action will skip using GitHub OIDC provider even if the id-token permission is set. | No |
|
||||||
|
| action-timeout-s | Global timeout for the action in seconds. If set to a value greater than 0, the action will fail if it takes longer than this time to complete. | No |
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
#### Adjust the retry mechanism
|
#### Adjust the retry mechanism
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ inputs:
|
|||||||
force-skip-oidc:
|
force-skip-oidc:
|
||||||
required: false
|
required: false
|
||||||
description: When enabled, this option will skip using GitHub OIDC provider even if the id-token permission is set. This is sometimes useful when using IAM instance credentials.
|
description: When enabled, this option will skip using GitHub OIDC provider even if the id-token permission is set. This is sometimes useful when using IAM instance credentials.
|
||||||
|
action-timeout-s:
|
||||||
|
required: false
|
||||||
|
description: A global timeout in seconds for the action. When the timeout is reached, the action immediately exits. The default is to run without a timeout.
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
aws-account-id:
|
aws-account-id:
|
||||||
|
|||||||
15
src/index.ts
15
src/index.ts
@@ -58,6 +58,16 @@ export async function run() {
|
|||||||
.map((s) => s.trim());
|
.map((s) => s.trim());
|
||||||
const forceSkipOidc = getBooleanInput('force-skip-oidc', { required: false });
|
const forceSkipOidc = getBooleanInput('force-skip-oidc', { required: false });
|
||||||
const noProxy = core.getInput('no-proxy', { required: false });
|
const noProxy = core.getInput('no-proxy', { required: false });
|
||||||
|
const globalTimeout = Number.parseInt(core.getInput('action-timeout-s', { required: false })) || 0;
|
||||||
|
|
||||||
|
let timeoutId: NodeJS.Timeout | undefined;
|
||||||
|
if (globalTimeout > 0) {
|
||||||
|
core.info(`Setting a global timeout of ${globalTimeout} seconds for the action`);
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
core.setFailed(`Action timed out after ${globalTimeout} seconds`);
|
||||||
|
process.exit(1);
|
||||||
|
}, globalTimeout * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
if (forceSkipOidc && roleToAssume && !AccessKeyId && !webIdentityTokenFile) {
|
if (forceSkipOidc && roleToAssume && !AccessKeyId && !webIdentityTokenFile) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -123,6 +133,7 @@ export async function run() {
|
|||||||
const validCredentials = await areCredentialsValid(credentialsClient);
|
const validCredentials = await areCredentialsValid(credentialsClient);
|
||||||
if (validCredentials) {
|
if (validCredentials) {
|
||||||
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
|
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
core.notice('No valid credentials exist. Running as normal.');
|
core.notice('No valid credentials exist. Running as normal.');
|
||||||
@@ -209,11 +220,13 @@ export async function run() {
|
|||||||
} else {
|
} else {
|
||||||
core.info('Proceeding with IAM user credentials');
|
core.info('Proceeding with IAM user credentials');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear timeout on successful completion
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(errorMessage(error));
|
core.setFailed(errorMessage(error));
|
||||||
|
|
||||||
const showStackTrace = process.env.SHOW_STACK_TRACE;
|
const showStackTrace = process.env.SHOW_STACK_TRACE;
|
||||||
|
|
||||||
if (showStackTrace === 'true') {
|
if (showStackTrace === 'true') {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -646,6 +646,86 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Global Timeout Configuration', {}, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
|
||||||
|
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
|
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValueOnce({
|
||||||
|
accessKeyId: 'MYAWSACCESSKEYID',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets timeout when action-timeout-s is provided', async () => {
|
||||||
|
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||||
|
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');
|
||||||
|
const infoSpy = vi.spyOn(core, 'info');
|
||||||
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
|
...mocks.IAM_USER_INPUTS,
|
||||||
|
'action-timeout-s': '30',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith('Setting a global timeout of 30 seconds for the action');
|
||||||
|
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 30000);
|
||||||
|
expect(clearTimeoutSpy).toHaveBeenCalledWith(expect.any(Object));
|
||||||
|
expect(core.setFailed).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not set timeout when action-timeout-s is 0', async () => {
|
||||||
|
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||||
|
const infoSpy = vi.spyOn(core, 'info');
|
||||||
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
|
...mocks.IAM_USER_INPUTS,
|
||||||
|
'action-timeout-s': '0',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(expect.stringContaining('Setting a global timeout'));
|
||||||
|
expect(setTimeoutSpy).not.toHaveBeenCalled();
|
||||||
|
expect(core.setFailed).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not set timeout when action-timeout-s is not provided', async () => {
|
||||||
|
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||||
|
const infoSpy = vi.spyOn(core, 'info');
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(expect.stringContaining('Setting a global timeout'));
|
||||||
|
expect(setTimeoutSpy).not.toHaveBeenCalled();
|
||||||
|
expect(core.setFailed).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('timeout callback calls setFailed and exits process', async () => {
|
||||||
|
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
||||||
|
const processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
||||||
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
|
...mocks.IAM_USER_INPUTS,
|
||||||
|
'action-timeout-s': '5',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
// Get the timeout callback function
|
||||||
|
const timeoutCallback = setTimeoutSpy.mock.calls[0][0] as () => void;
|
||||||
|
|
||||||
|
// Execute the timeout callback
|
||||||
|
timeoutCallback();
|
||||||
|
|
||||||
|
expect(core.setFailed).toHaveBeenCalledWith('Action timed out after 5 seconds');
|
||||||
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('HTTP Proxy Configuration', {}, () => {
|
describe('HTTP Proxy Configuration', {}, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||||
|
|||||||
Reference in New Issue
Block a user