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
13 changes: 10 additions & 3 deletions packages/aws-cdk/lib/api/hotswap/lambda-functions.ts
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
@@ -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',
});
});