Skip to content

Commit

Permalink
feat(cli): show all information from waiter errors (#33035)
Browse files Browse the repository at this point in the history
Waiter errors now have an additional piece of information: the responses
that were observed during the polling, and their counts.

Relay this information to the user in the error message.

Closes #32481.

----

*By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache-2.0 license*

(cherry picked from commit b512a72)
  • Loading branch information
otaviomacedo authored and moelasmar committed Jan 24, 2025
1 parent aeb51bc commit 2b1216d
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2547,6 +2547,8 @@ integTest(
// THEN
const expectedSubstring = 'Resource is not in the expected state due to waiter status: TIMEOUT';
expect(deployOutput).toContain(expectedSubstring);
expect(deployOutput).toContain('Observed responses:');
expect(deployOutput).toContain('200: OK');
expect(deployOutput).not.toContain('hotswapped!');
}),
);
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-cdk/THIRD_PARTY_LICENSES
Original file line number Diff line number Diff line change
Expand Up @@ -16464,7 +16464,7 @@ Apache License

----------------

** @smithy/service-error-classification@3.0.8 - https://www.npmjs.com/package/@smithy/service-error-classification/v/3.0.8 | Apache-2.0
** @smithy/service-error-classification@3.0.11 - https://www.npmjs.com/package/@smithy/service-error-classification/v/3.0.11 | Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Expand Down Expand Up @@ -20570,7 +20570,7 @@ Apache License

----------------

** @smithy/util-retry@3.0.8 - https://www.npmjs.com/package/@smithy/util-retry/v/3.0.8 | Apache-2.0
** @smithy/util-retry@3.0.11 - https://www.npmjs.com/package/@smithy/util-retry/v/3.0.11 | Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Expand Down
23 changes: 19 additions & 4 deletions packages/aws-cdk/lib/api/hotswap-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,7 @@ async function applyHotswappableChange(sdk: SDK, hotswapOperation: HotswappableC
} catch (e: any) {
if (e.name === 'TimeoutError' || e.name === 'AbortError') {
const result: WaiterResult = JSON.parse(formatErrorMessage(e));
const error = new ToolkitError([
`Resource is not in the expected state due to waiter status: ${result.state}`,
result.reason ? `${result.reason}.` : '',
].join('. '));
const error = new ToolkitError(formatWaiterErrorResult(result));
error.name = e.name;
throw error;
}
Expand All @@ -443,6 +440,24 @@ async function applyHotswappableChange(sdk: SDK, hotswapOperation: HotswappableC
sdk.removeCustomUserAgent(customUserAgent);
}

function formatWaiterErrorResult(result: WaiterResult) {
const main = [
`Resource is not in the expected state due to waiter status: ${result.state}`,
result.reason ? `${result.reason}.` : '',
].join('. ');

if (result.observedResponses != null) {
const observedResponses = Object
.entries(result.observedResponses)
.map(([msg, count]) => ` - ${msg} (${count})`)
.join('\n');

return `${main} Observed responses:\n${observedResponses}`;
}

return main;
}

function logNonHotswappableChanges(nonHotswappableChanges: NonHotswappableChange[], hotswapMode: HotswapMode): void {
if (nonHotswappableChanges.length === 0) {
return;
Expand Down
6 changes: 3 additions & 3 deletions packages/aws-cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@
"@smithy/property-provider": "^3.1.10",
"@smithy/shared-ini-file-loader": "^3.1.8",
"@smithy/types": "^3.5.0",
"@smithy/util-retry": "^3.0.7",
"@smithy/util-stream": "^3.1.9",
"@smithy/util-waiter": "^3.1.6",
"@smithy/util-retry": "^3.0.11",
"@smithy/util-stream": "^3.3.4",
"@smithy/util-waiter": "^3.2.0",
"archiver": "^5.3.2",
"camelcase": "^6.3.0",
"cdk-assets": "^3.0.0-rc.123",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { UpdateFunctionCodeCommand, waitUntilFunctionUpdatedV2 } from '@aws-sdk/client-lambda';
import * as setup from './hotswap-test-setup';
import { HotswapMode } from '../../../lib/api/hotswap/common';
import { mockLambdaClient } from '../../util/mock-sdk';
import { silentTest } from '../../util/silent';

let mockWaitUntilFunctionUpdatedV2: jest.Mock = jest.fn();
jest.mock('@aws-sdk/client-lambda', () => {
const original = jest.requireActual('@aws-sdk/client-lambda');

return {
...original,
waitUntilFunctionUpdatedV2: jest.fn(),
waitUntilFunctionUpdatedV2: mockWaitUntilFunctionUpdatedV2,
};
});

import { UpdateFunctionCodeCommand, waitUntilFunctionUpdatedV2 } from '@aws-sdk/client-lambda';
import * as setup from './hotswap-test-setup';
import { HotswapMode } from '../../../lib/api/hotswap/common';
import { mockLambdaClient } from '../../util/mock-sdk';
import { silentTest } from '../../util/silent';

let hotswapMockSdkProvider: setup.HotswapMockSdkProvider;

beforeEach(() => {
Expand Down Expand Up @@ -125,4 +126,61 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot
{ FunctionName: 'my-function' },
);
});

silentTest(
'throws error in case of timeout',
async () => {
// GIVEN
mockWaitUntilFunctionUpdatedV2.mockRejectedValue({
name: 'TimeoutError',
message: JSON.stringify({
state: 'TIMEOUT',
reason: 'Function not found',
observedResponses: {
'404: The function with name foo cannot be found.': 5,
},
}),
});
setup.setCurrentCfnStackTemplate({
Resources: {
Func: {
Type: 'AWS::Lambda::Function',
Properties: {
Code: {
ImageUri: 'current-image',
},
FunctionName: 'my-function',
},
Metadata: {
'aws:asset:path': 'old-path',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Func: {
Type: 'AWS::Lambda::Function',
Properties: {
Code: {
ImageUri: 'new-image',
},
FunctionName: 'my-function',
},
Metadata: {
'aws:asset:path': 'new-path',
},
},
},
},
});

// THEN
await expect(hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact))
.rejects
.toThrow(`Resource is not in the expected state due to waiter status: TIMEOUT. Function not found. Observed responses:
- 404: The function with name foo cannot be found. (5)`);
},
);
});
25 changes: 25 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8582,6 +8582,17 @@
"@smithy/util-base64" "^3.0.0"
tslib "^2.6.2"

"@smithy/fetch-http-handler@^4.1.3":
version "4.1.3"
resolved "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz#fc590dea2470d32559ae298306f1277729d24aa9"
integrity sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==
dependencies:
"@smithy/protocol-http" "^4.1.8"
"@smithy/querystring-builder" "^3.0.11"
"@smithy/types" "^3.7.2"
"@smithy/util-base64" "^3.0.0"
tslib "^2.6.2"

"@smithy/fetch-http-handler@^5.0.0", "@smithy/fetch-http-handler@^5.0.1":
version "5.0.1"
resolved "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz#8463393442ca6a1644204849e42c386066f0df79"
Expand Down Expand Up @@ -9852,6 +9863,20 @@
"@smithy/util-utf8" "^3.0.0"
tslib "^2.6.2"

"@smithy/util-stream@^3.3.4":
version "3.3.4"
resolved "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz#c506ac41310ebcceb0c3f0ba20755e4fe0a90b8d"
integrity sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==
dependencies:
"@smithy/fetch-http-handler" "^4.1.3"
"@smithy/node-http-handler" "^3.3.3"
"@smithy/types" "^3.7.2"
"@smithy/util-base64" "^3.0.0"
"@smithy/util-buffer-from" "^3.0.0"
"@smithy/util-hex-encoding" "^3.0.0"
"@smithy/util-utf8" "^3.0.0"
tslib "^2.6.2"

"@smithy/util-stream@^4.0.0", "@smithy/util-stream@^4.0.2":
version "4.0.2"
resolved "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.2.tgz#63495d3f7fba9d78748d540921136dc4a8d4c067"
Expand Down

0 comments on commit 2b1216d

Please sign in to comment.