From ce5b3cd43f2f0e2dc8a32f406e107bd5b201371b Mon Sep 17 00:00:00 2001 From: Diana Barsan Date: Thu, 25 Apr 2024 16:22:00 +0700 Subject: [PATCH 1/2] retry rate exceeded errors on up --- src/docker-compose-cli.js | 10 +++--- test/unit/docker-compose-cli.spec.js | 50 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/docker-compose-cli.js b/src/docker-compose-cli.js index 366ce53..98f7a47 100644 --- a/src/docker-compose-cli.js +++ b/src/docker-compose-cli.js @@ -36,20 +36,22 @@ const composeCommand = (filePaths, ...params) => { }); }; -const pull = async (fileName, retry = 100) => { +const retryCmdOnRateExceeded = async (filenames, command, retry = 100) => { try { - await composeCommand(fileName, 'pull'); + await composeCommand(filenames, command); } catch (err) { if (isRateExceededError(err) && retry > 0) { console.warn('Pull rate limit exceeded. Retrying.'); await new Promise(r => setTimeout(r, 1000)); - return pull(fileName, --retry); + return retryCmdOnRateExceeded(filenames, command, --retry); } throw err; } }; -const up = (fileNames) => composeCommand(fileNames, 'up -d --remove-orphans'); +const pull = async (fileName) => retryCmdOnRateExceeded(fileName, 'pull'); + +const up = (fileNames) => retryCmdOnRateExceeded(fileNames, 'up -d --remove-orphans'); const validate = async (fileName) => { try { diff --git a/test/unit/docker-compose-cli.spec.js b/test/unit/docker-compose-cli.spec.js index 03f16a8..41c62dc 100644 --- a/test/unit/docker-compose-cli.spec.js +++ b/test/unit/docker-compose-cli.spec.js @@ -132,6 +132,56 @@ describe('docker-compose cli', () => { expect(console.log.calledWith('thing2')).to.equal(true); }); + // https://docs.aws.amazon.com/AmazonECR/latest/userguide/common-errors.html + it('should retry on rate exceeded error', async () => { + const filename = 'path/to/file.yml'; + const result = dockerComposeCli.up([filename]); + + expect(childProcess.spawn.callCount).to.equal(1); + spawnedProcess.stderrCb('toomanyrequests: Rate exceeded'); + spawnedProcess.events.exit(1); + + await Promise.resolve(); + clock.tick(1000); + await Promise.resolve(); + + expect(childProcess.spawn.callCount).to.equal(2); + spawnedProcess.stderrCb('toomanyrequests: Rate exceeded'); + spawnedProcess.events.exit(1); + + await Promise.resolve(); + clock.tick(1000); + await Promise.resolve(); + + expect(childProcess.spawn.callCount).to.equal(3); + spawnedProcess.events.error({ message: 'Unknown: Rate exceeded' }); + + await Promise.resolve(); + clock.tick(1000); + await Promise.resolve(); + + expect(childProcess.spawn.callCount).to.equal(4); + spawnedProcess.events.exit(0); + + await result; + }); + + it('should throw error after 100 rate exceeded retries', async () => { + const filename = 'path/to/file.yml'; + const result = dockerComposeCli.up([filename]); + + for (let i = 0; i <= 100; i++) { + expect(childProcess.spawn.callCount).to.equal(i + 1); + spawnedProcess.stderrCb('toomanyrequests: Rate exceeded'); + spawnedProcess.events.exit(1); + + await Promise.resolve(); + clock.tick(1000); + await Promise.resolve(); + } + await expect(result).to.be.rejectedWith('toomanyrequests: Rate exceeded'); + }); + it('should reject on error', async () => { const filename = 'path/to/filename.yml'; const result = dockerComposeCli.up(filename); From 48e20efda8aba8d4239570e9093067247cd4be26 Mon Sep 17 00:00:00 2001 From: Diana Barsan Date: Thu, 25 Apr 2024 16:49:10 +0700 Subject: [PATCH 2/2] adds logging when running compose commands --- src/docker-compose-cli.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/docker-compose-cli.js b/src/docker-compose-cli.js index 98f7a47..18d3ba5 100644 --- a/src/docker-compose-cli.js +++ b/src/docker-compose-cli.js @@ -16,6 +16,7 @@ const composeCommand = (filePaths, ...params) => { ...filePaths.map(filePath => (['-f', filePath])), ...params.filter(param => param).map(param => param.split(' ')), ].flat(); + console.log(`Running cmd: ${DOCKER_CLI} ${args.join(' ')}`); return new Promise((resolve, reject) => { const proc = childProcess.spawn(DOCKER_CLI, args, { stdio: ['ignore', 'pipe', 'pipe'] });