From a9136489d11d37ee1af25b3538eeb0420377ecf0 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Wed, 26 Aug 2020 19:07:12 +0300 Subject: [PATCH 1/3] chore(mergify): fix status check name (#9987) Following up on https://github.com/aws/aws-cdk/pull/9928 which changed the name of the job from `mandatory-changes` to `validate-pr`. --- .mergify.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index 25c67f41b33ea..96cc7bbb7c21f 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -31,7 +31,7 @@ pull_request_rules: - "#changes-requested-reviews-by=0" - status-success~=AWS CodeBuild us-east-1 #- status-success=Semantic Pull Request - - status-success=mandatory-changes + - status-success=validate-pr - name: automatic merge (2+ approvers) actions: comment: @@ -56,7 +56,7 @@ pull_request_rules: - "#changes-requested-reviews-by=0" - status-success~=AWS CodeBuild us-east-1 #- status-success=Semantic Pull Request - - status-success=mandatory-changes + - status-success=validate-pr - name: automatic merge (no-squash) actions: comment: @@ -82,7 +82,7 @@ pull_request_rules: - "#changes-requested-reviews-by=0" - status-success~=AWS CodeBuild us-east-1 #- status-success=Semantic Pull Request - - status-success=mandatory-changes + - status-success=validate-pr - name: remove stale reviews actions: dismiss_reviews: @@ -126,4 +126,4 @@ pull_request_rules: - "#changes-requested-reviews-by=0" - status-success~=AWS CodeBuild us-east-1 #- status-success=Semantic Pull Request - - status-success=mandatory-changes + - status-success=validate-pr From 71c60f20300790bd8f4fa898c8855d93d37d7cd9 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Wed, 26 Aug 2020 21:23:23 +0100 Subject: [PATCH 2/3] fix(lambda): cannot use latest version in multiple cloudfront distributions (#9966) The error produced is around construct collision, i.e., "There is already a Construct with name '$LATEST'". The fix is to cache the latest version on the instance. fixes #4459 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-lambda/lib/function-base.ts | 8 ++++-- .../@aws-cdk/aws-lambda/test/test.function.ts | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 02b0e5d3285b1..8cbc0f241863a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -194,6 +194,8 @@ export abstract class FunctionBase extends Resource implements IFunction { */ protected _connections?: ec2.Connections; + private _latestVersion?: LatestVersion; + /** * Adds a permission to the Lambda resource policy. * @param id The id ƒor the permission construct @@ -244,8 +246,10 @@ export abstract class FunctionBase extends Resource implements IFunction { } public get latestVersion(): IVersion { - // Dynamic to avoid infinite recursion when creating the LatestVersion instance... - return new LatestVersion(this); + if (!this._latestVersion) { + this._latestVersion = new LatestVersion(this); + } + return this._latestVersion; } /** diff --git a/packages/@aws-cdk/aws-lambda/test/test.function.ts b/packages/@aws-cdk/aws-lambda/test/test.function.ts index 352d438e9c547..94c878161221b 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.function.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.function.ts @@ -359,6 +359,31 @@ export = testCase({ test.done(); }, + 'multiple calls to latestVersion returns the same version'(test: Test) { + const stack = new cdk.Stack(); + + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), + handler: 'index.hello', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version1 = fn.latestVersion; + const version2 = fn.latestVersion; + + const expectedArn = { + 'Fn::Join': ['', [ + { 'Fn::GetAtt': ['MyLambdaCCE802FB', 'Arn'] }, + ':$LATEST', + ]], + }; + test.equal(version1, version2); + test.deepEqual(stack.resolve(version1.functionArn), expectedArn); + test.deepEqual(stack.resolve(version2.functionArn), expectedArn); + + test.done(); + }, + 'currentVersion': { // see test.function-hash.ts for more coverage for this 'logical id of version is based on the function hash'(test: Test) { From e6db2a0ff686b6f045f440b1545711c7bdc1712c Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Wed, 26 Aug 2020 22:46:59 +0200 Subject: [PATCH 3/3] chore(lambda): current version for singleton functions (#9892) Add `currentVersion` for singleton functions. This makes it possible to use them for Lambda@Edge. To achieve this, make `ensureLambda()` return a `Function` and not an `IFunction` (which now allows to remove the default implementation of `_checkEdgeCompatibilty()` in `FunctionBase`). Also remove deprecated calls to `addVersion()` introduced in #9562. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cloudfront/test/distribution.test.ts | 36 +++++++++++++++++++ .../test/web_distribution.test.ts | 8 ++--- .../@aws-cdk/aws-lambda/lib/function-base.ts | 9 ----- .../@aws-cdk/aws-lambda/lib/lambda-version.ts | 4 +-- .../aws-lambda/lib/singleton-lambda.ts | 25 +++++++++---- .../aws-lambda/test/test.lambda-version.ts | 6 ++-- .../aws-lambda/test/test.singleton-lambda.ts | 24 +++++++++++++ 7 files changed, 87 insertions(+), 25 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts index 0d79aa18bf10b..0a8b5933e7f59 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts @@ -592,6 +592,42 @@ describe('with Lambda@Edge functions', () => { expect(() => app.synth()).toThrow(/KEY/); }); + + test('with singleton function', () => { + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: 'singleton-for-cloudfront', + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromInline('code'), + handler: 'index.handler', + }); + + new Distribution(stack, 'MyDist', { + defaultBehavior: { + origin, + edgeLambdas: [ + { + functionVersion: singleton.currentVersion, + eventType: LambdaEdgeEventType.ORIGIN_REQUEST, + }, + ], + }, + }); + + expect(stack).toHaveResourceLike('AWS::CloudFront::Distribution', { + DistributionConfig: { + DefaultCacheBehavior: { + LambdaFunctionAssociations: [ + { + EventType: 'origin-request', + LambdaFunctionARN: { + Ref: 'SingletonLambdasingletonforcloudfrontCurrentVersion0078406348a0962a52448a200cd0dbc0e22edb2a', + }, + }, + ], + }, + }, + }); + }); }); test('price class is included if provided', () => { diff --git a/packages/@aws-cdk/aws-cloudfront/test/web_distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/web_distribution.test.ts index e91c4e2f53192..c6cd639a7262a 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/web_distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/web_distribution.test.ts @@ -443,7 +443,7 @@ nodeunitShim({ isDefaultBehavior: true, lambdaFunctionAssociations: [{ eventType: LambdaEdgeEventType.ORIGIN_REQUEST, - lambdaFunction: lambdaFunction.addVersion('1'), + lambdaFunction: lambdaFunction.currentVersion, }], }, ], @@ -458,7 +458,7 @@ nodeunitShim({ { 'EventType': 'origin-request', 'LambdaFunctionARN': { - 'Ref': 'LambdaVersion1BB7548E1', + 'Ref': 'LambdaCurrentVersionDF706F6A97fb843e9bd06fcd2bb15eeace80e13e', }, }, ], @@ -492,7 +492,7 @@ nodeunitShim({ isDefaultBehavior: true, lambdaFunctionAssociations: [{ eventType: LambdaEdgeEventType.ORIGIN_REQUEST, - lambdaFunction: lambdaFunction.addVersion('1'), + lambdaFunction: lambdaFunction.currentVersion, }], }, ], @@ -532,7 +532,7 @@ nodeunitShim({ isDefaultBehavior: true, lambdaFunctionAssociations: [{ eventType: LambdaEdgeEventType.ORIGIN_REQUEST, - lambdaFunction: lambdaFunction.addVersion('1'), + lambdaFunction: lambdaFunction.currentVersion, }], }, ], diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 8cbc0f241863a..be0b5fffd4d39 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -324,15 +324,6 @@ export abstract class FunctionBase extends Resource implements IFunction { }); } - /** - * Checks whether this function is compatible for Lambda@Edge. - * - * @internal - */ - public _checkEdgeCompatibility(): void { - return; - } - /** * Returns the construct tree node that corresponds to the lambda function. * For use internally for constructs, when the tree is set up in non-standard ways. Ex: SingletonFunction. diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 17791a8d2ff7e..34ef68dc93e1a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -3,7 +3,7 @@ import { Construct, Fn, Lazy, RemovalPolicy } from '@aws-cdk/core'; import { Alias, AliasOptions } from './alias'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { Function } from './function'; -import { FunctionBase, IFunction, QualifiedFunctionBase } from './function-base'; +import { IFunction, QualifiedFunctionBase } from './function-base'; import { CfnVersion } from './lambda.generated'; import { addAlias } from './util'; @@ -253,7 +253,7 @@ export class Version extends QualifiedFunctionBase implements IVersion { return Lazy.stringValue({ produce: () => { // Validate that the underlying function can be used for Lambda@Edge - if (this.lambda instanceof FunctionBase) { + if (this.lambda instanceof Function) { this.lambda._checkEdgeCompatibility(); } diff --git a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts index 5cb9a84f88613..9a8e54472232a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts @@ -1,7 +1,8 @@ import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Function as LambdaFunction, FunctionProps } from './function'; -import { FunctionBase, IFunction } from './function-base'; +import { FunctionBase } from './function-base'; +import { Version } from './lambda-version'; import { Permission } from './permission'; /** @@ -45,7 +46,7 @@ export class SingletonFunction extends FunctionBase { public readonly role?: iam.IRole; public readonly permissionsNode: cdk.ConstructNode; protected readonly canCreatePermissions: boolean; - private lambdaFunction: IFunction; + private lambdaFunction: LambdaFunction; constructor(scope: cdk.Construct, id: string, props: SingletonFunctionProps) { super(scope, id); @@ -61,6 +62,18 @@ export class SingletonFunction extends FunctionBase { this.canCreatePermissions = true; // Doesn't matter, addPermission is overriden anyway } + /** + * Returns a `lambda.Version` which represents the current version of this + * singleton Lambda function. A new version will be created every time the + * function's configuration changes. + * + * You can specify options for this version using the `currentVersionOptions` + * prop when initializing the `lambda.SingletonFunction`. + */ + public get currentVersion(): Version { + return this.lambdaFunction.currentVersion; + } + public addPermission(name: string, permission: Permission) { return this.lambdaFunction.addPermission(name, permission); } @@ -83,9 +96,7 @@ export class SingletonFunction extends FunctionBase { /** @internal */ public _checkEdgeCompatibility() { - if (this.lambdaFunction instanceof FunctionBase) { - return this.lambdaFunction._checkEdgeCompatibility(); - } + return this.lambdaFunction._checkEdgeCompatibility(); } /** @@ -96,12 +107,12 @@ export class SingletonFunction extends FunctionBase { return this.lambdaFunction.node; } - private ensureLambda(props: SingletonFunctionProps): IFunction { + private ensureLambda(props: SingletonFunctionProps): LambdaFunction { const constructName = (props.lambdaPurpose || 'SingletonLambda') + slugify(props.uuid); const existing = cdk.Stack.of(this).node.tryFindChild(constructName); if (existing) { // Just assume this is true - return existing as FunctionBase; + return existing as LambdaFunction; } return new LambdaFunction(cdk.Stack.of(this), constructName, props); diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts index 8c9c4a45fd8e3..e06ce385522ea 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda-version.ts @@ -151,10 +151,10 @@ export = { handler: 'index.handler', code: lambda.Code.fromInline('foo'), }); - const version = fn.addVersion('1'); + const version = fn.currentVersion; // THEN - test.deepEqual(stack.resolve(version.edgeArn), { Ref: 'FnVersion1C3F5F93D' }); + test.deepEqual(stack.resolve(version.edgeArn), { Ref: 'FnCurrentVersion17A89ABB19ed45993ff69fd011ae9fd4ab6e2005' }); test.done(); }, @@ -179,7 +179,7 @@ export = { handler: 'index.handler', code: lambda.Code.fromInline('foo'), }); - const version = fn.addVersion('1'); + const version = fn.currentVersion; // WHEN new lambda.Function(stack, 'OtherFn', { diff --git a/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts index 6df79c0a278eb..0183e78badd27 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts @@ -160,4 +160,28 @@ export = { test.done(); }, + + 'current version of a singleton function'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('foo'), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + }); + + // WHEN + const version = singleton.currentVersion; + version.addAlias('foo'); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::Version', { + FunctionName: { + Ref: 'SingletonLambda84c0de93353f42179b0b45b6c993251a840BCC38', + }, + })); + + test.done(); + }, };