Skip to content

Commit

Permalink
fix(cli): lambda hotswap fails if lambda:GetFunctionConfiguration a…
Browse files Browse the repository at this point in the history
…ction is not allowed (#32301)

Closes #32219

### Reason for this change



In SDKv3, the standard `waitUntilFunctionUpdated` function invokes the `GetFunctionConfiguration` API, as opposed to SDKv2, which invoked `GetFunction`. This means that consumers of SDKv3 must allow the `lambda:GetFunctionConfiguration` action in their IAM role policy.

### Description of changes



Use a different waiter function provided by the SDK, which invokes `GetFunction` instead of `GetFunctionConfiguration`, and thus restoring required IAM permissions to what they were in SDKv2.

See https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-lambda/src/waiters/waitForFunctionUpdatedV2.ts#L10

> As opposed to https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-lambda/src/waiters/waitForFunctionUpdated.ts#L13

### Description of how you validated changes

Manul test. Assumed a role with the following policies:

![Screenshot 2024-11-27 at 9 34 25](https://github.com/user-attachments/assets/69415c37-6fe8-44d3-972c-1373ec55f46e)

```console
 ❯ cdk deploy --hotswap                                                                                                                                                                                                                                            [09:29:11]

✨  Synthesis time: 2.72s

⚠️ The --hotswap and --hotswap-fallback flags deliberately introduce CloudFormation drift to speed up deployments
⚠️ They should only be used for development - never use them for your production Stacks!

AwsCdkPlaygroundStack: deploying... [1/1]

✨ hotswapping resources:
   ✨ Lambda Function 'AwsCdkPlaygroundStack-Function76856677-7Rl7hiwwO5LQ'
❌  AwsCdkPlaygroundStack failed: TimeoutError: Resource is not in the expected state due to waiter status: TIMEOUT. Waiter has timed out.
```

Then, run the CLI from the PR.

```console
❯ /Users/epolon/dev/src/github.com/aws/aws-cdk/packages/aws-cdk/bin/cdk deploy --hotswap                                                                                                                                                                          [10:03:00]

✨  Synthesis time: 3.46s

⚠️ The --hotswap and --hotswap-fallback flags deliberately introduce CloudFormation drift to speed up deployments
⚠️ They should only be used for development - never use them for your production Stacks!

AwsCdkPlaygroundStack: deploying... [1/1]

✨ hotswapping resources:
   ✨ Lambda Function 'AwsCdkPlaygroundStack-Function76856677-7Rl7hiwwO5LQ'
✨ Lambda Function 'AwsCdkPlaygroundStack-Function76856677-7Rl7hiwwO5LQ' hotswapped!

 ✅  AwsCdkPlaygroundStack

✨  Deployment time: 12.72s

Stack ARN:
arn:aws:cloudformation:us-east-1:01234567890:stack/AwsCdkPlaygroundStack/22f2b380-a7cd-11ef-badd-0e08a8e0b5b1

✨  Total time: 16.19s

>>> elapsed time 23s                                                                                                                                                                                                                                                          
```



### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
iliapolo authored Nov 27, 2024
1 parent 6c0f74e commit be000a2
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 14 deletions.
4 changes: 2 additions & 2 deletions packages/aws-cdk/lib/api/aws-auth/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ import {
UpdateFunctionConfigurationCommand,
type UpdateFunctionConfigurationCommandInput,
type UpdateFunctionConfigurationCommandOutput,
waitUntilFunctionUpdated,
waitUntilFunctionUpdatedV2,
} from '@aws-sdk/client-lambda';
import {
GetHostedZoneCommand,
Expand Down Expand Up @@ -841,7 +841,7 @@ export class SDK {
delaySeconds: number,
input: UpdateFunctionConfigurationCommandInput,
): Promise<WaiterResult> => {
return waitUntilFunctionUpdated(
return waitUntilFunctionUpdatedV2(
{
client,
maxDelay: delaySeconds,
Expand Down
8 changes: 5 additions & 3 deletions packages/aws-cdk/test/api/hotswap/hotswap-test-setup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as cxapi from '@aws-cdk/cx-api';
import { ListStackResourcesCommand, StackResourceSummary, StackStatus } from '@aws-sdk/client-cloudformation';
import { GetFunctionConfigurationCommand } from '@aws-sdk/client-lambda';
import { GetFunctionCommand } from '@aws-sdk/client-lambda';
import { ICloudFormationClient, SuccessfulDeployStackResult } from '../../../lib/api';
import { HotswapMode, HotswapPropertyOverrides } from '../../../lib/api/hotswap/common';
import * as deployments from '../../../lib/api/hotswap-deployments';
Expand Down Expand Up @@ -109,8 +109,10 @@ export class HotswapMockSdkProvider extends MockSdkProvider {
constructor(rootStackName?: string) {
super();

mockLambdaClient.on(GetFunctionConfigurationCommand).resolves({
LastUpdateStatus: 'Successful',
mockLambdaClient.on(GetFunctionCommand).resolves({
Configuration: {
LastUpdateStatus: 'Successful',
},
});

mockCloudFormationClient.on(ListStackResourcesCommand).callsFake((input) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UpdateFunctionCodeCommand, waitUntilFunctionUpdated } from '@aws-sdk/client-lambda';
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';
Expand All @@ -9,7 +9,7 @@ jest.mock('@aws-sdk/client-lambda', () => {

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

Expand Down Expand Up @@ -116,7 +116,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot
await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);

// THEN
expect(waitUntilFunctionUpdated).toHaveBeenCalledWith(
expect(waitUntilFunctionUpdatedV2).toHaveBeenCalledWith(
expect.objectContaining({
minDelay: 5,
maxDelay: 5,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
UpdateFunctionCodeCommand,
UpdateFunctionConfigurationCommand,
waitUntilFunctionUpdated,
waitUntilFunctionUpdatedV2,
} from '@aws-sdk/client-lambda';
import * as setup from './hotswap-test-setup';
import { HotswapMode } from '../../../lib/api/hotswap/common';
Expand All @@ -10,10 +10,9 @@ import { silentTest } from '../../util/silent';

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

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

Expand Down Expand Up @@ -617,7 +616,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot

// THEN
expect(mockLambdaClient).toHaveReceivedCommand(UpdateFunctionCodeCommand);
expect(waitUntilFunctionUpdated).toHaveBeenCalledWith(
expect(waitUntilFunctionUpdatedV2).toHaveBeenCalledWith(
expect.objectContaining({
minDelay: 1,
maxDelay: 1,
Expand Down Expand Up @@ -675,7 +674,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot
await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);

// THEN
expect(waitUntilFunctionUpdated).toHaveBeenCalledWith(
expect(waitUntilFunctionUpdatedV2).toHaveBeenCalledWith(
expect.objectContaining({
minDelay: 1,
maxDelay: 1,
Expand Down Expand Up @@ -733,7 +732,7 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot
await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);

// THEN
expect(waitUntilFunctionUpdated).toHaveBeenCalledWith(
expect(waitUntilFunctionUpdatedV2).toHaveBeenCalledWith(
expect.objectContaining({
minDelay: 5,
maxDelay: 5,
Expand Down

0 comments on commit be000a2

Please sign in to comment.