From fa2369bbd3f19de5c59567b5395c921441b2e8c3 Mon Sep 17 00:00:00 2001 From: Reagan Elm Date: Thu, 4 Aug 2022 19:50:54 -0400 Subject: [PATCH] chore(aws-cdk): correct concurrent deploy error handling --- packages/aws-cdk/lib/deploy.ts | 50 +++++++++++++--------------- packages/aws-cdk/test/deploy.test.ts | 2 +- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/packages/aws-cdk/lib/deploy.ts b/packages/aws-cdk/lib/deploy.ts index 2db3008de74bf..95f45e8d93299 100644 --- a/packages/aws-cdk/lib/deploy.ts +++ b/packages/aws-cdk/lib/deploy.ts @@ -11,7 +11,6 @@ type DeploymentState = 'pending' | 'queued' | 'deploying' | 'completed' | 'faile export const deployStacks = async (stacks: cxapi.CloudFormationStackArtifact[], { concurrency, deployStack }: Options): Promise => { const queue = new PQueue({ concurrency }); const deploymentStates = stacks.reduce((acc, stack) => ({ ...acc, [stack.id]: 'pending' as const }), {} as Record); - const deployPromises: Promise[] = []; const isStackUnblocked = (stack: cxapi.CloudFormationStackArtifact) => stack.dependencies @@ -21,31 +20,32 @@ export const deployStacks = async (stacks: cxapi.CloudFormationStackArtifact[], const hasAnyStackFailed = (states: Record) => Object.values(states).includes('failed'); + const deploymentErrors: Error[] = []; + const enqueueStackDeploys = () => { stacks.forEach(async (stack) => { if (deploymentStates[stack.id] === 'pending' && isStackUnblocked(stack)) { deploymentStates[stack.id] = 'queued'; - deployPromises.push( - queue.add(async () => { - // Do not start new deployments if any has already failed - if (hasAnyStackFailed(deploymentStates)) { - deploymentStates[stack.id] = 'skipped'; - return; - } - - deploymentStates[stack.id] = 'deploying'; - try { - await deployStack(stack); - } catch (e) { - deploymentStates[stack.id] = 'failed'; - throw e; - } - - deploymentStates[stack.id] = 'completed'; - await enqueueStackDeploys(); - }), - ); + await queue.add(async () => { + // Do not start new deployments if any has already failed + if (hasAnyStackFailed(deploymentStates)) { + deploymentStates[stack.id] = 'skipped'; + return; + } + + deploymentStates[stack.id] = 'deploying'; + + await deployStack(stack).catch((err) => { + deploymentStates[stack.id] = 'failed'; + throw err; + }); + + deploymentStates[stack.id] = 'completed'; + enqueueStackDeploys(); + }).catch((err) => { + deploymentErrors.push(err); + }); } }); }; @@ -54,11 +54,7 @@ export const deployStacks = async (stacks: cxapi.CloudFormationStackArtifact[], await queue.onIdle(); - const results = await Promise.allSettled(deployPromises); - const isRejectedResult = (result: PromiseSettledResult): result is PromiseRejectedResult => result.status === 'rejected'; - const errors = results.filter(isRejectedResult).map(({ reason }) => reason); - - if (errors.length) { - throw Error(`Stack Deployments Failed: ${errors}`); + if (deploymentErrors.length) { + throw Error(`Stack Deployments Failed: ${deploymentErrors}`); } }; \ No newline at end of file diff --git a/packages/aws-cdk/test/deploy.test.ts b/packages/aws-cdk/test/deploy.test.ts index f3bc492349099..600f47531dd95 100644 --- a/packages/aws-cdk/test/deploy.test.ts +++ b/packages/aws-cdk/test/deploy.test.ts @@ -3,7 +3,7 @@ import { deployStacks } from '../lib/deploy'; type Stack = cxapi.CloudFormationStackArtifact; -const sleep = async (duration: number) => new Promise((resolve) => setTimeout(() => resolve(), duration)); +const sleep = async (duration: number) => new Promise((resolve) => setTimeout(() => resolve(), duration)); /** * Repurposing unused stack attributes to create specific test scenarios