From 8479e9040e4f1f60584fa3c9e88b3b03128cb069 Mon Sep 17 00:00:00 2001 From: Fedor Dikarev Date: Tue, 29 Oct 2024 22:03:00 +0100 Subject: [PATCH 1/4] add retries for 502 Signed-off-by: Fedor Dikarev --- src/context.ts | 4 +++- src/docker.ts | 37 +++++++++++++++++++++++++------------ src/main.ts | 2 +- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/context.ts b/src/context.ts index 8a381688..103287ef 100644 --- a/src/context.ts +++ b/src/context.ts @@ -6,6 +6,7 @@ export interface Inputs { password: string; ecr: string; logout: boolean; + attempts: number; } export function getInputs(): Inputs { @@ -14,6 +15,7 @@ export function getInputs(): Inputs { username: core.getInput('username'), password: core.getInput('password'), ecr: core.getInput('ecr'), - logout: core.getBooleanInput('logout') + logout: core.getBooleanInput('logout'), + attempts: Number.parseInt(core.getInput('attempts')) }; } diff --git a/src/docker.ts b/src/docker.ts index 5e2c1d5e..45d64a11 100644 --- a/src/docker.ts +++ b/src/docker.ts @@ -3,11 +3,11 @@ import * as core from '@actions/core'; import {Docker} from '@docker/actions-toolkit/lib/docker/docker'; -export async function login(registry: string, username: string, password: string, ecr: string): Promise { +export async function login(registry: string, username: string, password: string, ecr: string, attempts: number): Promise { if (/true/i.test(ecr) || (ecr == 'auto' && aws.isECR(registry))) { await loginECR(registry, username, password); } else { - await loginStandard(registry, username, password); + await loginStandard(registry, username, password, attempts); } } @@ -21,7 +21,7 @@ export async function logout(registry: string): Promise { }); } -export async function loginStandard(registry: string, username: string, password: string): Promise { +export async function loginStandard(registry: string, username: string, password: string, attempts: number): Promise { if (!username && !password) { throw new Error('Username and password required'); } @@ -41,16 +41,29 @@ export async function loginStandard(registry: string, username: string, password } else { core.info(`Logging into Docker Hub...`); } - await Docker.getExecOutput(loginArgs, { - ignoreReturnCode: true, - silent: true, - input: Buffer.from(password) - }).then(res => { - if (res.stderr.length > 0 && res.exitCode != 0) { - throw new Error(res.stderr.trim()); + let attempt: number = 1 + let succeeded: boolean = false + for (let attempt = 1; (attempt <= attempts) && (!succeeded); attempt++) { + await Docker.getExecOutput(loginArgs, { + ignoreReturnCode: true, + silent: true, + input: Buffer.from(password) + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + let isRetriable: boolean + isRetriable = res.stderr.endsWith("502 Bad Gateway") + if (!isRetriable || (attempt >= attempts) { + throw new Error(res.stderr.trim()); + } + } else { + core.info(`Login Succeeded!`); + succeeded = true; + } + }); + if ((attempt < attempts) && !succeeded) { + await new Promise(r => setTimeout(r, 10000)) } - core.info(`Login Succeeded!`); - }); + } } export async function loginECR(registry: string, username: string, password: string): Promise { diff --git a/src/main.ts b/src/main.ts index f35fa211..f8481c39 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,7 @@ export async function main(): Promise { const input: context.Inputs = context.getInputs(); stateHelper.setRegistry(input.registry); stateHelper.setLogout(input.logout); - await docker.login(input.registry, input.username, input.password, input.ecr); + await docker.login(input.registry, input.username, input.password, input.ecr, input.attempts); } async function post(): Promise { From 162c32cf05fa72e5fa5a2d788bb4c7e3914aa19e Mon Sep 17 00:00:00 2001 From: Fedor Dikarev Date: Tue, 29 Oct 2024 22:06:33 +0100 Subject: [PATCH 2/4] trim stderr for checking Signed-off-by: Fedor Dikarev --- src/docker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker.ts b/src/docker.ts index 45d64a11..632cd6ad 100644 --- a/src/docker.ts +++ b/src/docker.ts @@ -51,7 +51,7 @@ export async function loginStandard(registry: string, username: string, password }).then(res => { if (res.stderr.length > 0 && res.exitCode != 0) { let isRetriable: boolean - isRetriable = res.stderr.endsWith("502 Bad Gateway") + isRetriable = res.stderr.trim().endsWith("502 Bad Gateway") if (!isRetriable || (attempt >= attempts) { throw new Error(res.stderr.trim()); } From ca362a507b9064d055acf50a432af2400f0b4e0c Mon Sep 17 00:00:00 2001 From: Fedor Dikarev Date: Tue, 29 Oct 2024 22:28:24 +0100 Subject: [PATCH 3/4] add attempts to the action.yml Signed-off-by: Fedor Dikarev --- action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/action.yml b/action.yml index 3a0856d0..8a05613d 100644 --- a/action.yml +++ b/action.yml @@ -24,6 +24,10 @@ inputs: description: 'Log out from the Docker registry at the end of a job' default: 'true' required: false + attetmps: + description: 'Number of attempts to try in case of server-side errors' + default: '1' + required: false runs: using: 'node20' From 087884b3d26e0ea9f5fac62cfefae4831c7b96dc Mon Sep 17 00:00:00 2001 From: Fedor Dikarev Date: Wed, 30 Oct 2024 06:25:19 +0100 Subject: [PATCH 4/4] mistype Signed-off-by: Fedor Dikarev --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 8a05613d..04133d8f 100644 --- a/action.yml +++ b/action.yml @@ -24,7 +24,7 @@ inputs: description: 'Log out from the Docker registry at the end of a job' default: 'true' required: false - attetmps: + attempts: description: 'Number of attempts to try in case of server-side errors' default: '1' required: false