diff --git a/packages/aws-cdk/README.md b/packages/aws-cdk/README.md index 90f17b85e79b6..3d1e1b237921a 100644 --- a/packages/aws-cdk/README.md +++ b/packages/aws-cdk/README.md @@ -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. diff --git a/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts b/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts index b4bfc226410c1..14d96e85ae6d9 100644 --- a/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts +++ b/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts @@ -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) { @@ -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; } @@ -128,6 +132,7 @@ async function isLambdaFunctionCodeOnlyChange( code = { s3Bucket, s3Key, + imageUri, }; } break; @@ -165,8 +170,9 @@ interface CfnDiffTagValue { } interface LambdaFunctionCode { - readonly s3Bucket: string; - readonly s3Key: string; + readonly s3Bucket?: string; + readonly s3Key?: string; + readonly imageUri?: string; } enum TagDeletion { @@ -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 diff --git a/packages/aws-cdk/test/api/hotswap/lambda-functions-docker-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/lambda-functions-docker-hotswap-deployments.test.ts new file mode 100644 index 0000000000000..9aef8b8778cf0 --- /dev/null +++ b/packages/aws-cdk/test/api/hotswap/lambda-functions-docker-hotswap-deployments.test.ts @@ -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', + }); +});