diff --git a/src/cloudmanager-helpers.js b/src/cloudmanager-helpers.js index 54ead74..d92e163 100644 --- a/src/cloudmanager-helpers.js +++ b/src/cloudmanager-helpers.js @@ -14,7 +14,7 @@ const Config = require('@adobe/aio-lib-core-config') const { init } = require('@adobe/aio-lib-cloudmanager') const { cli } = require('cli-ux') const { context, getToken, Ims } = require('@adobe/aio-lib-ims') -const logger = require('@adobe/aio-lib-core-logging')('@adobe/aio-lib-cloudmanager', { provider: 'debug' }) +const logger = require('@adobe/aio-lib-core-logging')('@adobe/aio-lib-cloudmanager', { level: process.env.LOG_LEVEL }) const moment = require('moment') const _ = require('lodash') const { CLI } = require('@adobe/aio-lib-ims/src/context') @@ -338,6 +338,18 @@ function shouldResetRetires (startTime, resetInterval = 3600000) { return false } +async function executeWithRetry (fn, retries = 3, delay = 1000) { + for (let i = 0; i < retries; i++) { + try { + return await fn() + } catch (error) { + logger.debug(`Retrying due to error: ${error.message || 'Unknown error'} (attempt ${i + 1}/${retries})`) + if (i === retries - 1) throw error + await new Promise(resolve => setTimeout(resolve, delay)) + } + } +} + module.exports = { getProgramId, getOutputFormat, @@ -361,4 +373,5 @@ module.exports = { getFullOrgIdentity, handleError, executeWithRetries, + executeWithRetry, } diff --git a/src/commands/cloudmanager/environment/download-logs.js b/src/commands/cloudmanager/environment/download-logs.js index 9e9e7db..6ed3a8e 100644 --- a/src/commands/cloudmanager/environment/download-logs.js +++ b/src/commands/cloudmanager/environment/download-logs.js @@ -11,7 +11,7 @@ governing permissions and limitations under the License. */ const { flags } = require('@oclif/command') -const { initSdk, getProgramId, sanitizeEnvironmentId } = require('../../../cloudmanager-helpers') +const { initSdk, getProgramId, sanitizeEnvironmentId, executeWithRetry } = require('../../../cloudmanager-helpers') const { cli } = require('cli-ux') const path = require('path') const commonFlags = require('../../../common-flags') @@ -50,8 +50,10 @@ class DownloadLogs extends BaseCommand { } async downloadLogs (programId, environmentId, service, logName, days, outputDirectory, imsContextName = null) { - const sdk = await initSdk(imsContextName) - return sdk.downloadLogs(programId, environmentId, service, logName, days, outputDirectory) + return executeWithRetry(async () => { + const sdk = await initSdk(imsContextName) + return sdk.downloadLogs(programId, environmentId, service, logName, days, outputDirectory) + }) } } diff --git a/test/cloudmanager-helpers.test.js b/test/cloudmanager-helpers.test.js index 9f18600..24faf59 100644 --- a/test/cloudmanager-helpers.test.js +++ b/test/cloudmanager-helpers.test.js @@ -12,7 +12,7 @@ governing permissions and limitations under the License. const { setCurrentOrgId, context } = require('@adobe/aio-lib-ims') const { setStore } = require('@adobe/aio-lib-core-config') -const { initSdk, getOutputFormat, columnWithArray, disableCliAuth, enableCliAuth, formatDuration, executeWithRetries } = require('../src/cloudmanager-helpers') +const { initSdk, getOutputFormat, columnWithArray, disableCliAuth, enableCliAuth, formatDuration, executeWithRetries, executeWithRetry } = require('../src/cloudmanager-helpers') const { init } = require('@adobe/aio-lib-cloudmanager') const { setDecodedToken, resetDecodedToken } = require('jsonwebtoken') @@ -196,3 +196,36 @@ describe('executeWithRetries', () => { expect(mockFn.mock.calls.length).toEqual(6) }) }) + +describe('executeWithRetry', () => { + afterEach(() => { + jest.restoreAllMocks() + }) + + test('should not retry when function succeed', async () => { + const mockFn = jest.fn().mockResolvedValue('success') + + executeWithRetry(mockFn) + + expect(mockFn.mock.calls.length).toEqual(1) + }) + + test('should retry when error thrown', async () => { + const mockFn = jest.fn() + .mockRejectedValueOnce({}) + .mockResolvedValue('success') + + await executeWithRetry(mockFn) + + expect(mockFn.mock.calls.length).toEqual(2) + }) + + test('should throw error after 3 attempts', async () => { + const mockFn = jest.fn() + .mockRejectedValue(new Error()) + + await expect(executeWithRetry(mockFn)).rejects.toThrow() + + expect(mockFn.mock.calls.length).toEqual(3) + }) +})