Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): support hotswapping Lambda functions that use Docker images #18319

Merged
merged 11 commits into from
Jan 14, 2022
2 changes: 1 addition & 1 deletion packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ and that you have the necessary IAM permissions to update the resources that are
Hotswapping is currently supported for the following changes
(additional changes will be supported in the future):

- Code asset and tag changes of AWS Lambda functions.
- Code asset (including Docker image) and tag changes of AWS Lambda functions.
- AWS Lambda Versions and Aliases changes.
- Definition changes of AWS Step Functions State Machines.
- Container asset changes of AWS ECS Services.
Expand Down
13 changes: 10 additions & 3 deletions packages/aws-cdk/lib/api/hotswap/lambda-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ async function isLambdaFunctionCodeOnlyChange(
switch (updatedPropName) {
case 'Code':
let foundCodeDifference = false;
let s3Bucket = '', s3Key = '';
let s3Bucket, s3Key, imageUri;

for (const newPropName in updatedProp.newValue) {
switch (newPropName) {
Expand All @@ -120,6 +120,10 @@ async function isLambdaFunctionCodeOnlyChange(
foundCodeDifference = true;
s3Key = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]);
break;
case 'ImageUri':
foundCodeDifference = true;
imageUri = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]);
break;
default:
return ChangeHotswapImpact.REQUIRES_FULL_DEPLOYMENT;
}
Expand All @@ -128,6 +132,7 @@ async function isLambdaFunctionCodeOnlyChange(
code = {
s3Bucket,
s3Key,
imageUri,
};
}
break;
Expand Down Expand Up @@ -165,8 +170,9 @@ interface CfnDiffTagValue {
}

interface LambdaFunctionCode {
readonly s3Bucket: string;
readonly s3Key: string;
readonly s3Bucket?: string;
readonly s3Key?: string;
readonly imageUri?: string;
}
tmokmss marked this conversation as resolved.
Show resolved Hide resolved

enum TagDeletion {
Expand Down Expand Up @@ -214,6 +220,7 @@ class LambdaFunctionHotswapOperation implements HotswapOperation {
FunctionName: this.lambdaFunctionResource.physicalName,
S3Bucket: resource.code.s3Bucket,
S3Key: resource.code.s3Key,
ImageUri: resource.code.imageUri,
}).promise();

// only if the code changed is there any point in publishing a new Version
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Lambda } from 'aws-sdk';
import * as setup from './hotswap-test-setup';

let mockUpdateLambdaCode: (params: Lambda.Types.UpdateFunctionCodeRequest) => Lambda.Types.FunctionConfiguration;
let mockTagResource: (params: Lambda.Types.TagResourceRequest) => {};
let mockUntagResource: (params: Lambda.Types.UntagResourceRequest) => {};
let hotswapMockSdkProvider: setup.HotswapMockSdkProvider;

beforeEach(() => {
hotswapMockSdkProvider = setup.setupHotswapTests();
mockUpdateLambdaCode = jest.fn();
mockTagResource = jest.fn();
mockUntagResource = jest.fn();
hotswapMockSdkProvider.stubLambda({
updateFunctionCode: mockUpdateLambdaCode,
tagResource: mockTagResource,
untagResource: mockUntagResource,
});
});

test('calls the updateLambdaCode() API when it receives only a code difference in a Lambda function', async () => {
// GIVEN
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',
},
},
},
},
});

// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact);

// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mockUpdateLambdaCode).toHaveBeenCalledWith({
FunctionName: 'my-function',
ImageUri: 'new-image',
});
});