From 881f44d72e979ecb5b0aa9c19e508ce600889866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Dec 2018 11:35:57 +0100 Subject: [PATCH 1/8] feat(lambda): Support AWS Lambda Layers Add support for the AWS Lambda Layers feature, enabling users to define reusable and shareable packages of runtime code that can be shared across multiple lambda functions. --- packages/@aws-cdk/aws-lambda/README.md | 21 ++- packages/@aws-cdk/aws-lambda/lib/code.ts | 13 +- packages/@aws-cdk/aws-lambda/lib/index.ts | 1 + packages/@aws-cdk/aws-lambda/lib/lambda.ts | 22 +++ .../@aws-cdk/aws-lambda/lib/layer-version.ts | 163 ++++++++++++++++++ packages/@aws-cdk/aws-lambda/lib/runtime.ts | 4 + .../integ.layer-version.lit.expected.json | 129 ++++++++++++++ .../test/integ.layer-version.lit.ts | 34 ++++ .../test/integ.vpc-lambda.expected.json | 16 +- .../aws-lambda/test/layer-code/layer.ts | 3 + .../@aws-cdk/aws-lambda/test/test.lambda.ts | 42 ++++- .../aws-lambda/test/test.layer-version.ts | 71 ++++++++ 12 files changed, 497 insertions(+), 22 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda/lib/layer-version.ts create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts create mode 100644 packages/@aws-cdk/aws-lambda/test/layer-code/layer.ts create mode 100644 packages/@aws-cdk/aws-lambda/test/test.layer-version.ts diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index fb56ce6ceda5c..62ebae9d3253f 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -33,17 +33,24 @@ When deploying a stack that contains this code, the directory will be zip archived and then uploaded to an S3 bucket, then the exact location of the S3 objects will be passed when the stack is deployed. +### Layers + +The `lambda.LayerVersion` class can be used to define Lambda layers and manage +granting permissions to other AWS accounts or organizations. + +[Example of Lambda Layer usage](test/integ.layer-version.lit.ts) + ### Event Sources AWS Lambda supports a [variety of event sources](https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html). -In most cases, it is possible to trigger a function as a result of an event by -using one of the `onXxx` methods on the source construct. For example, the `s3.Bucket` -construct has an `onEvent` method which can be used to trigger a Lambda when an event, +In most cases, it is possible to trigger a function as a result of an event by +using one of the `onXxx` methods on the source construct. For example, the `s3.Bucket` +construct has an `onEvent` method which can be used to trigger a Lambda when an event, such as PutObject occurs on an S3 bucket. -An alternative way to add event sources to a function is to use `function.addEventSource(source)`. -This method accepts an `IEventSource` object. The module __@aws-cdk/aws-lambda-event-sources__ +An alternative way to add event sources to a function is to use `function.addEventSource(source)`. +This method accepts an `IEventSource` object. The module __@aws-cdk/aws-lambda-event-sources__ includes classes for the various event sources supported by AWS Lambda. For example, the following code adds an SQS queue as an event source for a function: @@ -90,7 +97,7 @@ fn.addToPipeline(lambdaStage, 'Lambda'); See [the AWS documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html) on how to write a Lambda function invoked from CodePipeline. -### Lambda with DLQ +### Lambda with DLQ ```ts import lambda = require('@aws-cdk/aws-lambda'); @@ -105,7 +112,7 @@ const fn = new lambda.Function(this, 'MyFunction', { See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/dlq.html) to learn more about AWS Lambdas and DLQs. -### Lambda with X-Ray Tracing +### Lambda with X-Ray Tracing ```ts import lambda = require('@aws-cdk/aws-lambda'); diff --git a/packages/@aws-cdk/aws-lambda/lib/code.ts b/packages/@aws-cdk/aws-lambda/lib/code.ts index 22237682d22ea..738027f2029f5 100644 --- a/packages/@aws-cdk/aws-lambda/lib/code.ts +++ b/packages/@aws-cdk/aws-lambda/lib/code.ts @@ -1,5 +1,6 @@ import assets = require('@aws-cdk/assets'); import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); import fs = require('fs'); import { Function as Func } from './lambda'; import { CfnFunction } from './lambda.generated'; @@ -57,10 +58,10 @@ export abstract class Code { public abstract toJSON(): CfnFunction.CodeProperty; /** - * Called when the lambda is initialized to allow this object to + * Called when the lambda or layer is initialized to allow this object to * bind to the stack, add resources and have fun. */ - public bind(_lambda: Func) { + public bind(_construct: cdk.Construct) { return; } } @@ -102,8 +103,8 @@ export class InlineCode extends Code { } } - public bind(lambda: Func) { - if (!lambda.runtime.supportsInlineCode) { + public bind(lambda: cdk.Construct) { + if (lambda instanceof Func && !lambda.runtime.supportsInlineCode) { throw new Error(`Inline source not allowed for ${lambda.runtime.name}`); } } @@ -142,10 +143,10 @@ export class AssetCode extends Code { } } - public bind(lambda: Func) { + public bind(construct: cdk.Construct) { // If the same AssetCode is used multiple times, retain only the first instantiation. if (!this.asset) { - this.asset = new assets.Asset(lambda, 'Code', { + this.asset = new assets.Asset(construct, 'Code', { path: this.path, packaging: this.packaging }); diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts index 1304dd96c2618..adf9aeb142e5c 100644 --- a/packages/@aws-cdk/aws-lambda/lib/index.ts +++ b/packages/@aws-cdk/aws-lambda/lib/index.ts @@ -1,6 +1,7 @@ export * from './alias'; export * from './lambda-ref'; export * from './lambda'; +export * from './layer-version'; export * from './permission'; export * from './pipeline-action'; export * from './runtime'; diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda.ts b/packages/@aws-cdk/aws-lambda/lib/lambda.ts index 237e8a9e6b4e0..9c2dff7db5af5 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda.ts @@ -6,6 +6,7 @@ import { Code } from './code'; import { FunctionRef } from './lambda-ref'; import { FunctionVersion } from './lambda-version'; import { CfnFunction } from './lambda.generated'; +import { LayerVersionRef } from './layer-version'; import { Runtime } from './runtime'; /** @@ -171,6 +172,15 @@ export interface FunctionProps { * @default undefined X-Ray tracing disabled */ tracing?: Tracing; + + /** + * A list of layers to add to the function's execution environment. You can configure your Lambda function to pull in + * additional code during initialization in the form of layers. Layers are packages of libraries or other dependencies + * that can be used by mulitple functions. + * + * @default no layers + */ + layerVersions?: LayerVersionRef[]; } /** @@ -220,6 +230,17 @@ export class Function extends FunctionRef { constructor(parent: cdk.Construct, name: string, props: FunctionProps) { super(parent, name); + if (props.layerVersions) { + if (props.layerVersions.length > 5) { + throw new Error(`A lambda function may only reference up to 5 layers at a time.`); + } + for (const layerVersion of props.layerVersions) { + if (layerVersion.compatibleRuntimes && layerVersion.compatibleRuntimes.indexOf(props.runtime) === -1) { + throw new Error(`The layer version defined at ${layerVersion.path} does not support the ${props.runtime} runtime.`); + } + } + } + this.environment = props.environment || { }; const managedPolicyArns = new Array(); @@ -245,6 +266,7 @@ export class Function extends FunctionRef { functionName: props.functionName, description: props.description, code: new cdk.Token(() => props.code.toJSON()), + layers: props.layerVersions && props.layerVersions.map(layer => layer.layerVersionArn), handler: props.handler, timeout: props.timeout, runtime: props.runtime.name, diff --git a/packages/@aws-cdk/aws-lambda/lib/layer-version.ts b/packages/@aws-cdk/aws-lambda/lib/layer-version.ts new file mode 100644 index 0000000000000..26c51e3be07ef --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/lib/layer-version.ts @@ -0,0 +1,163 @@ +import cdk = require('@aws-cdk/cdk'); +import { Code, InlineCode } from './code'; +import { Runtime } from './runtime'; + +export interface LayerVersionProps { + /** + * The runtimes that this layer is compatible with. + * + * @default All runtimes are supported + */ + compatibleRuntimes?: Runtime[]; + + /** + * The content of this Layer. Using the ``InlineCode`` class is not permitted. + */ + code: Code; + + /** + * The description the this Lambda Layer. + */ + description?: string; + + /** + * The SPDX licence identifier or URL to the license file for this layer. + */ + license?: string; + + /** + * The name of the layer. + * @default a name will be generated. + */ + name?: string; +} + +/** + * A reference to a Lambda Layer version. + */ +export abstract class LayerVersionRef extends cdk.Construct { + /** + * Imports a Layer that has been defined externally. + * + * @param parent the parent Construct that will use the imported layer. + * @param id the id of the imported layer in the construct tree. + * @param props the properties of the imported layer. + */ + public static import(parent: cdk.Construct, id: string, props: LayerVersionRefProps): LayerVersionRef { + return new ImportedLayerVersionRef(parent, id, props); + } + + /** + * The ARN of the Lambda Layer version that this Layer defines. + */ + public abstract readonly layerVersionArn: string; + + /** + * The runtimes compatible with this Layer. + */ + public abstract readonly compatibleRuntimes?: Runtime[]; + + /** + * Grants usage of this layer to specific entities. Usage within the same account where the layer is defined is always + * allowed and does not require calling this method. Note that the principal that creates the Lambda function using + * the layer (for example, a CloudFormation changeset execution role) also needs to have the + * ``lambda:GetLayerVersion`` permission on the layer version. + * + * @param awsAccountId the AWS account ID that will be granted access to the layer, or '*' to grant usage to any/all + * AWS accounts. The default is '*'. + * @param organizationId when using '*' as the ``awsAccountId``, a organization ID can be procided to restrict usage + * to accounts that are memeber of a given organization. + */ + public grantUsage(awsAccountId: string = '*', organizationId?: string): this { + if (organizationId != null && awsAccountId !== '*') { + throw new Error(`OrganizationId can only be specified if AwsAccountId is '*', but it is ${awsAccountId}`); + } + + new cdk.Resource(this, `grant-usage-${awsAccountId}-${organizationId || '*'}`, { + type: 'AWS::Lambda::LayerVersionPermission', + properties: { + Action: 'lambda:GetLayerVersion', + LayerVersionArn: this.layerVersionArn, + Principal: awsAccountId, + OrganizationId: organizationId, + } + }); + return this; + } + + /** + * Exports this layer for use in another Stack. The resulting object can be passed to the ``LayerRef.import`` function + * to obtain a ``LayerRef`` in the user stack. + */ + public export(): LayerVersionRefProps { + return { + layerVersionArn: new cdk.Output(this, 'LayerVersionArn', { value: this.layerVersionArn }).makeImportValue().toString(), + compatibleRuntimes: this.compatibleRuntimes, + }; + } +} + +/** + * Properties necessary to import a LayerRef. + */ +export interface LayerVersionRefProps { + /** + * The ARN of the LayerVersion. + */ + layerVersionArn: string; + + /** + * The list of compatible runtimes with this Layer. + */ + compatibleRuntimes?: Runtime[]; +} + +/** + * Defines a new Lambda Layer version. + */ +export class LayerVersion extends LayerVersionRef { + public readonly layerVersionArn: string; + public readonly compatibleRuntimes?: Runtime[]; + + constructor(parent: cdk.Construct, id: string, props: LayerVersionProps) { + super(parent, id); + if (props.compatibleRuntimes && props.compatibleRuntimes.length === 0) { + throw new Error('Attempted to define a Lambda layer that supports no runtime!'); + } + if (props.code instanceof InlineCode) { + throw new Error('Lambda layers cannot be created from inline code'); + } + // Allow usage of the code in this context... + props.code.bind(this); + + const resource = new cdk.Resource(this, 'Resource', { + type: 'AWS::Lambda::LayerVersion', + properties: { + CompatibleRuntimes: props.compatibleRuntimes && props.compatibleRuntimes.map(r => r.name), + Content: props.code.toJSON(), + Description: props.description, + LayerName: props.name, + LicenseInfo: props.license, + } + }); + + this.layerVersionArn = resource.ref; + this.compatibleRuntimes = props.compatibleRuntimes; + } +} + +class ImportedLayerVersionRef extends LayerVersionRef { + public readonly layerVersionArn: string; + public readonly compatibleRuntimes?: Runtime[]; + + public constructor(parent: cdk.Construct, id: string, props: LayerVersionRefProps) { + super(parent, id); + + if (props.compatibleRuntimes && props.compatibleRuntimes.length === 0) { + throw new Error('Attempted to import a Lambda layer that supports no runtime!'); + } + + this.layerVersionArn = props.layerVersionArn; + this.compatibleRuntimes = props.compatibleRuntimes; + } +} diff --git a/packages/@aws-cdk/aws-lambda/lib/runtime.ts b/packages/@aws-cdk/aws-lambda/lib/runtime.ts index 1cf5cfc559668..33583fa2a2e74 100644 --- a/packages/@aws-cdk/aws-lambda/lib/runtime.ts +++ b/packages/@aws-cdk/aws-lambda/lib/runtime.ts @@ -22,6 +22,8 @@ export enum RuntimeFamily { * can instantiate a `Runtime` object, e.g: `new Runtime('nodejs99.99')`. */ export class Runtime { + public static readonly All = new Array(); + public static readonly NodeJS = new Runtime('nodejs', RuntimeFamily.NodeJS, { supportsInlineCode: true }); public static readonly NodeJS43 = new Runtime('nodejs4.3', RuntimeFamily.NodeJS, { supportsInlineCode: true }); public static readonly NodeJS610 = new Runtime('nodejs6.10', RuntimeFamily.NodeJS, { supportsInlineCode: true }); @@ -56,6 +58,8 @@ export class Runtime { this.name = name; this.supportsInlineCode = !!props.supportsInlineCode; this.family = family; + + Runtime.All.push(this); } public toString(): string { diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json new file mode 100644 index 0000000000000..b574fa3230332 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json @@ -0,0 +1,129 @@ +{ + "Parameters": { + "MyLayerCodeS3Bucket20DB8EB9": { + "Type": "String", + "Description": "S3 bucket for asset \"aws-cdk-layer-version-1/MyLayer/Code\"" + }, + "MyLayerCodeS3VersionKeyA45254EC": { + "Type": "String", + "Description": "S3 key for asset version \"aws-cdk-layer-version-1/MyLayer/Code\"" + } + }, + "Resources": { + "MyLayer38944FA5": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "compatibleRuntimes": [ + "nodejs8.10" + ], + "content": { + "s3Bucket": { + "Ref": "MyLayerCodeS3Bucket20DB8EB9" + }, + "s3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "MyLayerCodeS3VersionKeyA45254EC" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "MyLayerCodeS3VersionKeyA45254EC" + } + ] + } + ] + } + ] + ] + } + }, + "description": "A layer to test the L2 construct", + "licenseInfo": "Apache-2.0" + } + }, + "MyLayergrantusageTokenAWSAccountId0202BA27F": { + "Type": "AWS::Lambda::LayerVersionPermission", + "Properties": { + "action": "lambda:GetLayerVersion", + "layerVersionArn": { + "Ref": "MyLayer38944FA5" + }, + "principal": { + "Ref": "AWS::AccountId" + } + } + }, + "MyLayeredLambdaServiceRole1A7DC118": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MyLayeredLambda9A3008D1": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLayeredLambdaServiceRole1A7DC118", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Layers": [ + { + "Ref": "MyLayer38944FA5" + } + ] + }, + "DependsOn": [ + "MyLayeredLambdaServiceRole1A7DC118" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts new file mode 100644 index 0000000000000..d41d2681053b2 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts @@ -0,0 +1,34 @@ +import cdk = require('@aws-cdk/cdk'); +import path = require('path'); +import lambda = require('../lib'); + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-layer-version-1'); + +// Just for the example - granting to the current account is not necessary. +const awsAccountId = new cdk.AwsAccountId().toString(); + +/// !show +const layer = new lambda.LayerVersion(stack, 'MyLayer', { + code: lambda.Code.directory(path.join(__dirname, 'layer-code')), + compatibleRuntimes: [lambda.Runtime.NodeJS810], + license: 'Apache-2.0', + description: 'A layer to test the L2 construct', +}); + +// To grant usage by other AWS accounts +layer.grantUsage(awsAccountId); + +// To grant usage to all accounts in some AWS Ogranization +// layer.grantUsage('*', organizationId); + +new lambda.Function(stack, 'MyLayeredLambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NodeJS810, + layerVersions: [layer], +}); +/// !hide + +app.run(); diff --git a/packages/@aws-cdk/aws-lambda/test/integ.vpc-lambda.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.vpc-lambda.expected.json index 3325043c8414e..f432ae88789d3 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.vpc-lambda.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.vpc-lambda.expected.json @@ -67,9 +67,6 @@ }, "VPCPublicSubnet1DefaultRoute91CEF279": { "Type": "AWS::EC2::Route", - "DependsOn": [ - "VPCVPCGW99B986DC" - ], "Properties": { "RouteTableId": { "Ref": "VPCPublicSubnet1RouteTableFEE4B781" @@ -78,7 +75,10 @@ "GatewayId": { "Ref": "VPCIGWB7E252D3" } - } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] }, "VPCPublicSubnet1EIP6AD938E8": { "Type": "AWS::EC2::EIP", @@ -158,9 +158,6 @@ }, "VPCPublicSubnet2DefaultRouteB7481BBA": { "Type": "AWS::EC2::Route", - "DependsOn": [ - "VPCVPCGW99B986DC" - ], "Properties": { "RouteTableId": { "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" @@ -169,7 +166,10 @@ "GatewayId": { "Ref": "VPCIGWB7E252D3" } - } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] }, "VPCPublicSubnet2EIP4947BC00": { "Type": "AWS::EC2::EIP", diff --git a/packages/@aws-cdk/aws-lambda/test/layer-code/layer.ts b/packages/@aws-cdk/aws-lambda/test/layer-code/layer.ts new file mode 100644 index 0000000000000..de36deb9a37a2 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/layer-code/layer.ts @@ -0,0 +1,3 @@ +export async function main(_event: any, _context: any) { + return 'Done!'; +} diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index ebcca95f660e6..22caebbf188c6 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -1121,7 +1121,47 @@ export = { Runtime: 'ruby2.5' }, DependsOn: [ 'MyLambdaServiceRole4539ECB6' ] } } }); test.done(); - } + }, + + 'using an incompatible layer'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, 'TestStack'); + const layer = lambda.LayerVersionRef.import(stack, 'TestLayer', { + layerVersionArn: 'arn:aws:...', + compatibleRuntimes: [lambda.Runtime.NodeJS810], + }); + + // THEN + test.throws(() => new lambda.Function(stack, 'Function', { + layerVersions: [layer], + runtime: lambda.Runtime.NodeJS610, + code: lambda.Code.inline('exports.main = function() { console.log("DONE"); }'), + handler: 'index.main' + }), + /does not support the nodejs6.10 runtime/); + + test.done(); + }, + + 'using more than 5 layers'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, 'TestStack'); + const layers = new Array(6).map(() => lambda.LayerVersionRef.import(stack, 'TestLayer', { + layerVersionArn: 'arn:aws:...', + compatibleRuntimes: [lambda.Runtime.NodeJS810], + })); + + // THEN + test.throws(() => new lambda.Function(stack, 'Function', { + layerVersions: layers, + runtime: lambda.Runtime.NodeJS810, + code: lambda.Code.inline('exports.main = function() { console.log("DONE"); }'), + handler: 'index.main' + }), + /up to 5 layers/); + + test.done(); + }, }; function newTestLambda(parent: cdk.Construct) { diff --git a/packages/@aws-cdk/aws-lambda/test/test.layer-version.ts b/packages/@aws-cdk/aws-lambda/test/test.layer-version.ts new file mode 100644 index 0000000000000..acfed290ff09b --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/test.layer-version.ts @@ -0,0 +1,71 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import { Test, testCase } from 'nodeunit'; +import lambda = require('../lib'); + +export = testCase({ + 'creating a layer'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, 'TestStack'); + const bucket = new s3.Bucket(stack, 'Bucket'); + const code = new lambda.S3Code(bucket, 'ObjectKey'); + + // WHEN + new lambda.LayerVersion(stack, 'LayerVersion', { + code, + compatibleRuntimes: [lambda.Runtime.NodeJS810] + }); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::LayerVersion', { + Content: cdk.resolve(code.toJSON()), + CompatibleRuntimes: ['nodejs8.10'] + })); + + test.done(); + }, + + 'granting access to a layer'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, 'TestStack'); + const bucket = new s3.Bucket(stack, 'Bucket'); + const code = new lambda.S3Code(bucket, 'ObjectKey'); + const layer = new lambda.LayerVersion(stack, 'LayerVersion', { + code, + compatibleRuntimes: [lambda.Runtime.NodeJS810] + }); + + // WHEN + layer.grantUsage('123456789012'); + layer.grantUsage('*', 'o-123456'); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::LayerVersionPermission', { + Action: 'lambda:GetLayerVersion', + LayerVersionArn: cdk.resolve(layer.layerVersionArn), + Principal: '123456789012', + })); + expect(stack).to(haveResource('AWS::Lambda::LayerVersionPermission', { + Action: 'lambda:GetLayerVersion', + LayerVersionArn: cdk.resolve(layer.layerVersionArn), + Principal: '*', + OrganizationId: 'o-123456' + })); + + test.done(); + }, + + 'creating a layer with no runtimes compatible'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, 'TestStack'); + const bucket = new s3.Bucket(stack, 'Bucket'); + const code = new lambda.S3Code(bucket, 'ObjectKey'); + + // THEN + test.throws(() => new lambda.LayerVersion(stack, 'LayerVersion', { code, compatibleRuntimes: [] }), + /supports no runtime/); + + test.done(); + }, +}); From 43213e75498af3deac6dd21fe877c9a79c328c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Dec 2018 12:35:16 +0100 Subject: [PATCH 2/8] Added SingletonLayerVersion, addressed most of PR comments --- packages/@aws-cdk/aws-lambda/lib/code.ts | 8 +- packages/@aws-cdk/aws-lambda/lib/index.ts | 2 +- packages/@aws-cdk/aws-lambda/lib/lambda.ts | 12 +-- .../lib/{layer-version.ts => layers.ts} | 86 ++++++++++++++++--- packages/@aws-cdk/aws-lambda/lib/runtime.ts | 1 + .../integ.layer-version.lit.expected.json | 14 +-- .../test/integ.layer-version.lit.ts | 6 +- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 4 +- .../{test.layer-version.ts => test.layers.ts} | 24 +++++- 9 files changed, 121 insertions(+), 36 deletions(-) rename packages/@aws-cdk/aws-lambda/lib/{layer-version.ts => layers.ts} (63%) rename packages/@aws-cdk/aws-lambda/test/{test.layer-version.ts => test.layers.ts} (73%) diff --git a/packages/@aws-cdk/aws-lambda/lib/code.ts b/packages/@aws-cdk/aws-lambda/lib/code.ts index 738027f2029f5..64574069ac0eb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/code.ts +++ b/packages/@aws-cdk/aws-lambda/lib/code.ts @@ -2,7 +2,6 @@ import assets = require('@aws-cdk/assets'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); import fs = require('fs'); -import { Function as Func } from './lambda'; import { CfnFunction } from './lambda.generated'; export abstract class Code { @@ -103,9 +102,10 @@ export class InlineCode extends Code { } } - public bind(lambda: cdk.Construct) { - if (lambda instanceof Func && !lambda.runtime.supportsInlineCode) { - throw new Error(`Inline source not allowed for ${lambda.runtime.name}`); + public bind(construct: cdk.Construct) { + const runtime = (construct as any).runtime; + if (!runtime.supportsInlineCode) { + throw new Error(`Inline source not allowed for ${runtime && runtime.name}`); } } diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts index adf9aeb142e5c..dd5d5fd46e668 100644 --- a/packages/@aws-cdk/aws-lambda/lib/index.ts +++ b/packages/@aws-cdk/aws-lambda/lib/index.ts @@ -1,7 +1,7 @@ export * from './alias'; export * from './lambda-ref'; export * from './lambda'; -export * from './layer-version'; +export * from './layers'; export * from './permission'; export * from './pipeline-action'; export * from './runtime'; diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda.ts b/packages/@aws-cdk/aws-lambda/lib/lambda.ts index 9c2dff7db5af5..8f136b5614361 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda.ts @@ -6,7 +6,7 @@ import { Code } from './code'; import { FunctionRef } from './lambda-ref'; import { FunctionVersion } from './lambda-version'; import { CfnFunction } from './lambda.generated'; -import { LayerVersionRef } from './layer-version'; +import { LayerVersionRef } from './layers'; import { Runtime } from './runtime'; /** @@ -180,7 +180,7 @@ export interface FunctionProps { * * @default no layers */ - layerVersions?: LayerVersionRef[]; + layers?: LayerVersionRef[]; } /** @@ -230,11 +230,11 @@ export class Function extends FunctionRef { constructor(parent: cdk.Construct, name: string, props: FunctionProps) { super(parent, name); - if (props.layerVersions) { - if (props.layerVersions.length > 5) { + if (props.layers) { + if (props.layers.length > 5) { throw new Error(`A lambda function may only reference up to 5 layers at a time.`); } - for (const layerVersion of props.layerVersions) { + for (const layerVersion of props.layers) { if (layerVersion.compatibleRuntimes && layerVersion.compatibleRuntimes.indexOf(props.runtime) === -1) { throw new Error(`The layer version defined at ${layerVersion.path} does not support the ${props.runtime} runtime.`); } @@ -266,7 +266,7 @@ export class Function extends FunctionRef { functionName: props.functionName, description: props.description, code: new cdk.Token(() => props.code.toJSON()), - layers: props.layerVersions && props.layerVersions.map(layer => layer.layerVersionArn), + layers: props.layers && props.layers.map(layer => layer.layerVersionArn), handler: props.handler, timeout: props.timeout, runtime: props.runtime.name, diff --git a/packages/@aws-cdk/aws-lambda/lib/layer-version.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts similarity index 63% rename from packages/@aws-cdk/aws-lambda/lib/layer-version.ts rename to packages/@aws-cdk/aws-lambda/lib/layers.ts index 26c51e3be07ef..1f06837257b75 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layer-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -63,23 +63,20 @@ export abstract class LayerVersionRef extends cdk.Construct { * the layer (for example, a CloudFormation changeset execution role) also needs to have the * ``lambda:GetLayerVersion`` permission on the layer version. * - * @param awsAccountId the AWS account ID that will be granted access to the layer, or '*' to grant usage to any/all - * AWS accounts. The default is '*'. - * @param organizationId when using '*' as the ``awsAccountId``, a organization ID can be procided to restrict usage - * to accounts that are memeber of a given organization. + * @param grantee the identification of the grantee. */ - public grantUsage(awsAccountId: string = '*', organizationId?: string): this { - if (organizationId != null && awsAccountId !== '*') { - throw new Error(`OrganizationId can only be specified if AwsAccountId is '*', but it is ${awsAccountId}`); + public grantUsage(grantee: LayerVersionUsageGrantee): LayerVersionRef { + if (grantee.organizationId != null && grantee.accountId !== '*') { + throw new Error(`OrganizationId can only be specified if AwsAccountId is '*', but it is ${grantee.accountId}`); } - new cdk.Resource(this, `grant-usage-${awsAccountId}-${organizationId || '*'}`, { + new cdk.Resource(this, `grant-usage-${grantee.accountId}-${grantee.organizationId || '*'}`, { type: 'AWS::Lambda::LayerVersionPermission', properties: { Action: 'lambda:GetLayerVersion', LayerVersionArn: this.layerVersionArn, - Principal: awsAccountId, - OrganizationId: organizationId, + Principal: grantee.accountId, + OrganizationId: grantee.organizationId, } }); return this; @@ -97,6 +94,24 @@ export abstract class LayerVersionRef extends cdk.Construct { } } +/** + * Identification of an account (or organization) that is allowed to access a Lambda Layer Version. + */ +export interface LayerVersionUsageGrantee { + /** + * The AWS Account id of the account that is authorized to use a Lambda Layer Version. The wild-card ``'*'`` can be + * used to grant access to "any" account (or any account in an organization when ``organizationId`` is specified). + */ + accountId: string; + + /** + * The ID of the AWS Organization to hwich the grant is restricted. + * + * Can only be specified if ``accountId`` is ``'*'`` + */ + organizationId?: string; +} + /** * Properties necessary to import a LayerRef. */ @@ -161,3 +176,54 @@ class ImportedLayerVersionRef extends LayerVersionRef { this.compatibleRuntimes = props.compatibleRuntimes; } } + +/** + * Properties of a Singleton Lambda Layer Version. + */ +export interface SingletonLayerVersionProps extends LayerVersionProps { + /** + * A unique identifier to identify this lambda layer version. + * + * The identifier should be unique across all layer providers. + * We recommend generating a UUID per provider. + */ + uuid: string; +} + +/** + * A Singleton Lambda Layer Version. The construct gurantees exactly one LayerVersion will be created in a given Stack + * for the provided ``uuid``. It is recommended to use ``uuidgen`` to create a new ``uuid`` each time a new singleton + * layer is created. + */ +export class SingletonLayerVersion extends LayerVersionRef { + private readonly layerVersion: LayerVersionRef; + + constructor(parent: cdk.Construct, id: string, props: SingletonLayerVersionProps) { + super(parent, id); + + this.layerVersion = this.ensureLayerVersion(props); + } + + public get layerVersionArn(): string { + return this.layerVersion.layerVersionArn; + } + + public get compatibleRuntimes(): Runtime[] | undefined { + return this.layerVersion.compatibleRuntimes; + } + + public grantUsage(grantee: LayerVersionUsageGrantee): LayerVersionRef { + this.layerVersion.grantUsage(grantee); + return this; + } + + private ensureLayerVersion(props: SingletonLayerVersionProps): LayerVersionRef { + const singletonId = `SingletonLayer-${props.uuid}`; + const stack = cdk.Stack.find(this); + const existing = stack.tryFindChild(singletonId); + if (existing) { + return existing as LayerVersionRef; + } + return new LayerVersion(stack, singletonId, props); + } +} diff --git a/packages/@aws-cdk/aws-lambda/lib/runtime.ts b/packages/@aws-cdk/aws-lambda/lib/runtime.ts index 33583fa2a2e74..e5a4ab552996c 100644 --- a/packages/@aws-cdk/aws-lambda/lib/runtime.ts +++ b/packages/@aws-cdk/aws-lambda/lib/runtime.ts @@ -22,6 +22,7 @@ export enum RuntimeFamily { * can instantiate a `Runtime` object, e.g: `new Runtime('nodejs99.99')`. */ export class Runtime { + /** A list of all the know ``Runtime``s. */ public static readonly All = new Array(); public static readonly NodeJS = new Runtime('nodejs', RuntimeFamily.NodeJS, { supportsInlineCode: true }); diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json index b574fa3230332..a63b88433d1d4 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json @@ -13,10 +13,10 @@ "MyLayer38944FA5": { "Type": "AWS::Lambda::LayerVersion", "Properties": { - "compatibleRuntimes": [ + "CompatibleRuntimes": [ "nodejs8.10" ], - "content": { + "Content": { "s3Bucket": { "Ref": "MyLayerCodeS3Bucket20DB8EB9" }, @@ -54,18 +54,18 @@ ] } }, - "description": "A layer to test the L2 construct", - "licenseInfo": "Apache-2.0" + "Description": "A layer to test the L2 construct", + "LicenseInfo": "Apache-2.0" } }, "MyLayergrantusageTokenAWSAccountId0202BA27F": { "Type": "AWS::Lambda::LayerVersionPermission", "Properties": { - "action": "lambda:GetLayerVersion", - "layerVersionArn": { + "Action": "lambda:GetLayerVersion", + "LayerVersionArn": { "Ref": "MyLayer38944FA5" }, - "principal": { + "Principal": { "Ref": "AWS::AccountId" } } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts index d41d2681053b2..30aaef1455939 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts @@ -18,16 +18,16 @@ const layer = new lambda.LayerVersion(stack, 'MyLayer', { }); // To grant usage by other AWS accounts -layer.grantUsage(awsAccountId); +layer.grantUsage({ accountId: awsAccountId }); // To grant usage to all accounts in some AWS Ogranization -// layer.grantUsage('*', organizationId); +// layer.grantUsage({ accountId: '*', organizationId }); new lambda.Function(stack, 'MyLayeredLambda', { code: new lambda.InlineCode('foo'), handler: 'index.handler', runtime: lambda.Runtime.NodeJS810, - layerVersions: [layer], + layers: [layer], }); /// !hide diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 22caebbf188c6..523f7b44ebfe0 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -1133,7 +1133,7 @@ export = { // THEN test.throws(() => new lambda.Function(stack, 'Function', { - layerVersions: [layer], + layers: [layer], runtime: lambda.Runtime.NodeJS610, code: lambda.Code.inline('exports.main = function() { console.log("DONE"); }'), handler: 'index.main' @@ -1153,7 +1153,7 @@ export = { // THEN test.throws(() => new lambda.Function(stack, 'Function', { - layerVersions: layers, + layers, runtime: lambda.Runtime.NodeJS810, code: lambda.Code.inline('exports.main = function() { console.log("DONE"); }'), handler: 'index.main' diff --git a/packages/@aws-cdk/aws-lambda/test/test.layer-version.ts b/packages/@aws-cdk/aws-lambda/test/test.layers.ts similarity index 73% rename from packages/@aws-cdk/aws-lambda/test/test.layer-version.ts rename to packages/@aws-cdk/aws-lambda/test/test.layers.ts index acfed290ff09b..5e3a5c6135942 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.layer-version.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.layers.ts @@ -1,4 +1,4 @@ -import { expect, haveResource } from '@aws-cdk/assert'; +import { countResources, expect, haveResource } from '@aws-cdk/assert'; import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); import { Test, testCase } from 'nodeunit'; @@ -37,8 +37,8 @@ export = testCase({ }); // WHEN - layer.grantUsage('123456789012'); - layer.grantUsage('*', 'o-123456'); + layer.grantUsage({ accountId: '123456789012' }); + layer.grantUsage({ accountId: '*', organizationId: 'o-123456' }); // THEN expect(stack).to(haveResource('AWS::Lambda::LayerVersionPermission', { @@ -68,4 +68,22 @@ export = testCase({ test.done(); }, + + 'singleton layers are created exactly once'(test: Test) { + // Given + const stack = new cdk.Stack(undefined, 'TestStack'); + const uuid = '75F9D74A-67AF-493E-888A-20976130F0B1'; + const bucket = new s3.Bucket(stack, 'Bucket'); + const code = new lambda.S3Code(bucket, 'ObjectKey'); + + // When + for (let i = 0 ; i < 5 ; i++) { + new lambda.SingletonLayerVersion(stack, `Layer-${i}`, { uuid, code }); + } + + // Then + expect(stack).to(countResources('AWS::Lambda::LayerVersion', 1)); + + test.done(); + } }); From d0205898bb03ffce9ccef68416b144c2b9665a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Dec 2018 12:52:52 +0100 Subject: [PATCH 3/8] Remove use of instanceof --- packages/@aws-cdk/aws-lambda/lib/code.ts | 10 ++++++++++ packages/@aws-cdk/aws-lambda/lib/layers.ts | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/code.ts b/packages/@aws-cdk/aws-lambda/lib/code.ts index 64574069ac0eb..ba21c90558f96 100644 --- a/packages/@aws-cdk/aws-lambda/lib/code.ts +++ b/packages/@aws-cdk/aws-lambda/lib/code.ts @@ -50,6 +50,11 @@ export abstract class Code { return new AssetCode(filePath, assets.AssetPackaging.File); } + /** + * Determines whether this Code is inline code or not. + */ + public abstract readonly isInline: boolean; + /** * Called during stack synthesis to render the CodePropery for the * Lambda function. @@ -69,6 +74,7 @@ export abstract class Code { * Lambda code from an S3 archive. */ export class S3Code extends Code { + public readonly isInline = false; private bucketName: string; constructor(bucket: s3.BucketRef, private key: string, private objectVersion?: string) { @@ -94,6 +100,8 @@ export class S3Code extends Code { * Lambda code from an inline string (limited to 4KiB). */ export class InlineCode extends Code { + public readonly isInline = true; + constructor(private code: string) { super(); @@ -120,6 +128,8 @@ export class InlineCode extends Code { * Lambda code from a local directory. */ export class AssetCode extends Code { + public readonly isInline = false; + /** * The asset packaging. */ diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 1f06837257b75..760764e30bceb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -1,5 +1,5 @@ import cdk = require('@aws-cdk/cdk'); -import { Code, InlineCode } from './code'; +import { Code } from './code'; import { Runtime } from './runtime'; export interface LayerVersionProps { @@ -11,7 +11,7 @@ export interface LayerVersionProps { compatibleRuntimes?: Runtime[]; /** - * The content of this Layer. Using the ``InlineCode`` class is not permitted. + * The content of this Layer. Using the *inline* code is not permitted. */ code: Code; @@ -139,7 +139,7 @@ export class LayerVersion extends LayerVersionRef { if (props.compatibleRuntimes && props.compatibleRuntimes.length === 0) { throw new Error('Attempted to define a Lambda layer that supports no runtime!'); } - if (props.code instanceof InlineCode) { + if (props.code.isInline) { throw new Error('Lambda layers cannot be created from inline code'); } // Allow usage of the code in this context... From 14126bf18a10d41a7f84b8a2828fcc6596682297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Dec 2018 13:03:47 +0100 Subject: [PATCH 4/8] Use new import style --- packages/@aws-cdk/aws-lambda/lib/lambda.ts | 6 +- packages/@aws-cdk/aws-lambda/lib/layers.ts | 57 +++++++++++-------- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 4 +- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda.ts b/packages/@aws-cdk/aws-lambda/lib/lambda.ts index 8f136b5614361..2e5fba374c2c0 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda.ts @@ -6,7 +6,7 @@ import { Code } from './code'; import { FunctionRef } from './lambda-ref'; import { FunctionVersion } from './lambda-version'; import { CfnFunction } from './lambda.generated'; -import { LayerVersionRef } from './layers'; +import { ILayerVersion } from './layers'; import { Runtime } from './runtime'; /** @@ -180,7 +180,7 @@ export interface FunctionProps { * * @default no layers */ - layers?: LayerVersionRef[]; + layers?: ILayerVersion[]; } /** @@ -236,7 +236,7 @@ export class Function extends FunctionRef { } for (const layerVersion of props.layers) { if (layerVersion.compatibleRuntimes && layerVersion.compatibleRuntimes.indexOf(props.runtime) === -1) { - throw new Error(`The layer version defined at ${layerVersion.path} does not support the ${props.runtime} runtime.`); + throw new Error(`The layer version ${layerVersion} does not support the ${props.runtime} runtime.`); } } } diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 760764e30bceb..628fc0749ab53 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -32,30 +32,16 @@ export interface LayerVersionProps { name?: string; } -/** - * A reference to a Lambda Layer version. - */ -export abstract class LayerVersionRef extends cdk.Construct { - /** - * Imports a Layer that has been defined externally. - * - * @param parent the parent Construct that will use the imported layer. - * @param id the id of the imported layer in the construct tree. - * @param props the properties of the imported layer. - */ - public static import(parent: cdk.Construct, id: string, props: LayerVersionRefProps): LayerVersionRef { - return new ImportedLayerVersionRef(parent, id, props); - } - +export interface ILayerVersion { /** * The ARN of the Lambda Layer version that this Layer defines. */ - public abstract readonly layerVersionArn: string; + readonly layerVersionArn: string; /** * The runtimes compatible with this Layer. */ - public abstract readonly compatibleRuntimes?: Runtime[]; + readonly compatibleRuntimes?: Runtime[]; /** * Grants usage of this layer to specific entities. Usage within the same account where the layer is defined is always @@ -65,7 +51,17 @@ export abstract class LayerVersionRef extends cdk.Construct { * * @param grantee the identification of the grantee. */ - public grantUsage(grantee: LayerVersionUsageGrantee): LayerVersionRef { + grantUsage(grantee: LayerVersionUsageGrantee): ILayerVersion +} + +/** + * A reference to a Lambda Layer version. + */ +export abstract class LayerVersionBase extends cdk.Construct implements ILayerVersion { + public abstract readonly layerVersionArn: string; + public abstract readonly compatibleRuntimes?: Runtime[]; + + public grantUsage(grantee: LayerVersionUsageGrantee): ILayerVersion { if (grantee.organizationId != null && grantee.accountId !== '*') { throw new Error(`OrganizationId can only be specified if AwsAccountId is '*', but it is ${grantee.accountId}`); } @@ -130,7 +126,18 @@ export interface LayerVersionRefProps { /** * Defines a new Lambda Layer version. */ -export class LayerVersion extends LayerVersionRef { +export class LayerVersion extends LayerVersionBase { + /** + * Imports a Layer that has been defined externally. + * + * @param parent the parent Construct that will use the imported layer. + * @param id the id of the imported layer in the construct tree. + * @param props the properties of the imported layer. + */ + public static import(parent: cdk.Construct, id: string, props: LayerVersionRefProps): ILayerVersion { + return new ImportedLayerVersionRef(parent, id, props); + } + public readonly layerVersionArn: string; public readonly compatibleRuntimes?: Runtime[]; @@ -161,7 +168,7 @@ export class LayerVersion extends LayerVersionRef { } } -class ImportedLayerVersionRef extends LayerVersionRef { +class ImportedLayerVersionRef extends LayerVersionBase { public readonly layerVersionArn: string; public readonly compatibleRuntimes?: Runtime[]; @@ -195,8 +202,8 @@ export interface SingletonLayerVersionProps extends LayerVersionProps { * for the provided ``uuid``. It is recommended to use ``uuidgen`` to create a new ``uuid`` each time a new singleton * layer is created. */ -export class SingletonLayerVersion extends LayerVersionRef { - private readonly layerVersion: LayerVersionRef; +export class SingletonLayerVersion extends cdk.Construct implements ILayerVersion { + private readonly layerVersion: ILayerVersion; constructor(parent: cdk.Construct, id: string, props: SingletonLayerVersionProps) { super(parent, id); @@ -212,17 +219,17 @@ export class SingletonLayerVersion extends LayerVersionRef { return this.layerVersion.compatibleRuntimes; } - public grantUsage(grantee: LayerVersionUsageGrantee): LayerVersionRef { + public grantUsage(grantee: LayerVersionUsageGrantee): ILayerVersion { this.layerVersion.grantUsage(grantee); return this; } - private ensureLayerVersion(props: SingletonLayerVersionProps): LayerVersionRef { + private ensureLayerVersion(props: SingletonLayerVersionProps): ILayerVersion { const singletonId = `SingletonLayer-${props.uuid}`; const stack = cdk.Stack.find(this); const existing = stack.tryFindChild(singletonId); if (existing) { - return existing as LayerVersionRef; + return existing as unknown as ILayerVersion; } return new LayerVersion(stack, singletonId, props); } diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 523f7b44ebfe0..1afb9391445ad 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -1126,7 +1126,7 @@ export = { 'using an incompatible layer'(test: Test) { // GIVEN const stack = new cdk.Stack(undefined, 'TestStack'); - const layer = lambda.LayerVersionRef.import(stack, 'TestLayer', { + const layer = lambda.LayerVersion.import(stack, 'TestLayer', { layerVersionArn: 'arn:aws:...', compatibleRuntimes: [lambda.Runtime.NodeJS810], }); @@ -1146,7 +1146,7 @@ export = { 'using more than 5 layers'(test: Test) { // GIVEN const stack = new cdk.Stack(undefined, 'TestStack'); - const layers = new Array(6).map(() => lambda.LayerVersionRef.import(stack, 'TestLayer', { + const layers = new Array(6).map(() => lambda.LayerVersion.import(stack, 'TestLayer', { layerVersionArn: 'arn:aws:...', compatibleRuntimes: [lambda.Runtime.NodeJS810], })); From be2dc01747fd1864fc8e8fc8950a86700708d1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Dec 2018 13:09:05 +0100 Subject: [PATCH 5/8] Some renaming --- packages/@aws-cdk/aws-lambda/lib/layers.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 628fc0749ab53..047eb52f6d78a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -79,10 +79,10 @@ export abstract class LayerVersionBase extends cdk.Construct implements ILayerVe } /** - * Exports this layer for use in another Stack. The resulting object can be passed to the ``LayerRef.import`` function - * to obtain a ``LayerRef`` in the user stack. + * Exports this layer for use in another Stack. The resulting object can be passed to the ``LayerVersion.import`` + * function to obtain an ``ILayerVersion`` in the user stack. */ - public export(): LayerVersionRefProps { + public export(): ImportedLayerVersionProps { return { layerVersionArn: new cdk.Output(this, 'LayerVersionArn', { value: this.layerVersionArn }).makeImportValue().toString(), compatibleRuntimes: this.compatibleRuntimes, @@ -109,9 +109,9 @@ export interface LayerVersionUsageGrantee { } /** - * Properties necessary to import a LayerRef. + * Properties necessary to import a LayerVersion. */ -export interface LayerVersionRefProps { +export interface ImportedLayerVersionProps { /** * The ARN of the LayerVersion. */ @@ -134,8 +134,8 @@ export class LayerVersion extends LayerVersionBase { * @param id the id of the imported layer in the construct tree. * @param props the properties of the imported layer. */ - public static import(parent: cdk.Construct, id: string, props: LayerVersionRefProps): ILayerVersion { - return new ImportedLayerVersionRef(parent, id, props); + public static import(parent: cdk.Construct, id: string, props: ImportedLayerVersionProps): ILayerVersion { + return new ImportedLayerVersion(parent, id, props); } public readonly layerVersionArn: string; @@ -168,11 +168,11 @@ export class LayerVersion extends LayerVersionBase { } } -class ImportedLayerVersionRef extends LayerVersionBase { +class ImportedLayerVersion extends LayerVersionBase { public readonly layerVersionArn: string; public readonly compatibleRuntimes?: Runtime[]; - public constructor(parent: cdk.Construct, id: string, props: LayerVersionRefProps) { + public constructor(parent: cdk.Construct, id: string, props: ImportedLayerVersionProps) { super(parent, id); if (props.compatibleRuntimes && props.compatibleRuntimes.length === 0) { From d8fa683ca513cc30ea7bdc4d01f41ce7d399ea72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 27 Dec 2018 10:48:46 +0100 Subject: [PATCH 6/8] Fix broken tests --- packages/@aws-cdk/aws-lambda/lib/layers.ts | 11 ++++++----- .../test/integ.layer-version.lit.expected.json | 2 +- .../aws-lambda/test/integ.layer-version.lit.ts | 2 +- packages/@aws-cdk/aws-lambda/test/test.layers.ts | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 047eb52f6d78a..222187816b040 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -50,8 +50,9 @@ export interface ILayerVersion { * ``lambda:GetLayerVersion`` permission on the layer version. * * @param grantee the identification of the grantee. + * @param grantId the ID of the grant in the construct tree. */ - grantUsage(grantee: LayerVersionUsageGrantee): ILayerVersion + grantUsage(grantee: LayerVersionUsageGrantee, grantId: string): ILayerVersion } /** @@ -61,12 +62,12 @@ export abstract class LayerVersionBase extends cdk.Construct implements ILayerVe public abstract readonly layerVersionArn: string; public abstract readonly compatibleRuntimes?: Runtime[]; - public grantUsage(grantee: LayerVersionUsageGrantee): ILayerVersion { + public grantUsage(grantee: LayerVersionUsageGrantee, grantId: string): ILayerVersion { if (grantee.organizationId != null && grantee.accountId !== '*') { throw new Error(`OrganizationId can only be specified if AwsAccountId is '*', but it is ${grantee.accountId}`); } - new cdk.Resource(this, `grant-usage-${grantee.accountId}-${grantee.organizationId || '*'}`, { + new cdk.Resource(this, grantId, { type: 'AWS::Lambda::LayerVersionPermission', properties: { Action: 'lambda:GetLayerVersion', @@ -219,8 +220,8 @@ export class SingletonLayerVersion extends cdk.Construct implements ILayerVersio return this.layerVersion.compatibleRuntimes; } - public grantUsage(grantee: LayerVersionUsageGrantee): ILayerVersion { - this.layerVersion.grantUsage(grantee); + public grantUsage(grantee: LayerVersionUsageGrantee, grantId: string): ILayerVersion { + this.layerVersion.grantUsage(grantee, grantId); return this; } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json index a63b88433d1d4..c75ea2f19d958 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json @@ -58,7 +58,7 @@ "LicenseInfo": "Apache-2.0" } }, - "MyLayergrantusageTokenAWSAccountId0202BA27F": { + "MyLayerremoteaccountgrant715E5D21": { "Type": "AWS::Lambda::LayerVersionPermission", "Properties": { "Action": "lambda:GetLayerVersion", diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts index 30aaef1455939..8bc7fc0cd8b5d 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts @@ -18,7 +18,7 @@ const layer = new lambda.LayerVersion(stack, 'MyLayer', { }); // To grant usage by other AWS accounts -layer.grantUsage({ accountId: awsAccountId }); +layer.grantUsage({ accountId: awsAccountId }, 'remote-account-grant'); // To grant usage to all accounts in some AWS Ogranization // layer.grantUsage({ accountId: '*', organizationId }); diff --git a/packages/@aws-cdk/aws-lambda/test/test.layers.ts b/packages/@aws-cdk/aws-lambda/test/test.layers.ts index 5e3a5c6135942..92aaaeef6b888 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.layers.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.layers.ts @@ -37,8 +37,8 @@ export = testCase({ }); // WHEN - layer.grantUsage({ accountId: '123456789012' }); - layer.grantUsage({ accountId: '*', organizationId: 'o-123456' }); + layer.grantUsage({ accountId: '123456789012' }, 'GrantUsage-123456789012'); + layer.grantUsage({ accountId: '*', organizationId: 'o-123456' }, 'GrantUsage-o-123456'); // THEN expect(stack).to(haveResource('AWS::Lambda::LayerVersionPermission', { From 93342542b94443199e9eb17861845e45045f338a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 27 Dec 2018 11:16:54 +0100 Subject: [PATCH 7/8] Fix more broken tests. --- packages/@aws-cdk/aws-lambda/lib/lambda.ts | 43 +++++++++++++------ .../@aws-cdk/aws-lambda/test/test.lambda.ts | 6 +-- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda.ts b/packages/@aws-cdk/aws-lambda/lib/lambda.ts index 2e5fba374c2c0..86e0a0633c2c7 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda.ts @@ -222,6 +222,8 @@ export class Function extends FunctionRef { protected readonly canCreatePermissions = true; + private readonly layers: ILayerVersion[] = []; + /** * Environment variables for this function */ @@ -230,17 +232,6 @@ export class Function extends FunctionRef { constructor(parent: cdk.Construct, name: string, props: FunctionProps) { super(parent, name); - if (props.layers) { - if (props.layers.length > 5) { - throw new Error(`A lambda function may only reference up to 5 layers at a time.`); - } - for (const layerVersion of props.layers) { - if (layerVersion.compatibleRuntimes && layerVersion.compatibleRuntimes.indexOf(props.runtime) === -1) { - throw new Error(`The layer version ${layerVersion} does not support the ${props.runtime} runtime.`); - } - } - } - this.environment = props.environment || { }; const managedPolicyArns = new Array(); @@ -266,7 +257,7 @@ export class Function extends FunctionRef { functionName: props.functionName, description: props.description, code: new cdk.Token(() => props.code.toJSON()), - layers: props.layers && props.layers.map(layer => layer.layerVersionArn), + layers: new cdk.Token(() => this.layers.length > 0 ? this.layers.map(layer => layer.layerVersionArn) : undefined), handler: props.handler, timeout: props.timeout, runtime: props.runtime.name, @@ -287,6 +278,10 @@ export class Function extends FunctionRef { // allow code to bind to stack. props.code.bind(this); + + for (const layer of props.layers || []) { + this.addLayer(layer); + } } /** @@ -295,12 +290,32 @@ export class Function extends FunctionRef { * @param key The environment variable key. * @param value The environment variable's value. */ - public addEnvironment(key: string, value: any) { + public addEnvironment(key: string, value: any): this { if (!this.environment) { // TODO: add metadata - return; + return this; } this.environment[key] = value; + return this; + } + + /** + * Adds a Lambda Layer to this Lambda function. + * + * @param layer the layer to be added. + * + * @throws if there are already 5 layers on this function, or the layer is incompatible with this function's runtime. + */ + public addLayer(layer: ILayerVersion): this { + if (this.layers.length === 5) { + throw new Error('Unable to add layer: this lambda function already uses 5 layers.'); + } + if (layer.compatibleRuntimes && layer.compatibleRuntimes.indexOf(this.runtime) === -1) { + const runtimes = layer.compatibleRuntimes.map(runtime => runtime.name).join(', '); + throw new Error(`This lambda function uses a runtime that is incompatible with this layer (${this.runtime.name} is not in [${runtimes}])`); + } + this.layers.push(layer); + return this; } /** diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 1afb9391445ad..1616893ba303b 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -1138,7 +1138,7 @@ export = { code: lambda.Code.inline('exports.main = function() { console.log("DONE"); }'), handler: 'index.main' }), - /does not support the nodejs6.10 runtime/); + /nodejs6.10 is not in \[nodejs8.10\]/); test.done(); }, @@ -1146,7 +1146,7 @@ export = { 'using more than 5 layers'(test: Test) { // GIVEN const stack = new cdk.Stack(undefined, 'TestStack'); - const layers = new Array(6).map(() => lambda.LayerVersion.import(stack, 'TestLayer', { + const layers = new Array(6).fill(lambda.LayerVersion.import(stack, 'TestLayer', { layerVersionArn: 'arn:aws:...', compatibleRuntimes: [lambda.Runtime.NodeJS810], })); @@ -1158,7 +1158,7 @@ export = { code: lambda.Code.inline('exports.main = function() { console.log("DONE"); }'), handler: 'index.main' }), - /up to 5 layers/); + /Unable to add layer:/); test.done(); }, From 7232445ec1bb8b78a168503e665a51c8499441e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Wed, 23 Jan 2019 16:36:36 -0800 Subject: [PATCH 8/8] Merge latest and greatest! --- packages/@aws-cdk/aws-lambda/lib/code.ts | 16 +++++++---- packages/@aws-cdk/aws-lambda/lib/lambda.ts | 2 +- packages/@aws-cdk/aws-lambda/lib/layers.ts | 28 ++++++++++--------- .../test/integ.layer-version.lit.ts | 4 +-- .../@aws-cdk/aws-lambda/test/test.layers.ts | 10 +++---- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/code.ts b/packages/@aws-cdk/aws-lambda/lib/code.ts index ee939f3329633..e252d32fe675a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/code.ts +++ b/packages/@aws-cdk/aws-lambda/lib/code.ts @@ -58,8 +58,10 @@ export abstract class Code { /** * Called during stack synthesis to render the CodePropery for the * Lambda function. + * + * @param resource the resource to which the code will be attached (a CfnFunction, or a CfnLayerVersion). */ - public abstract toJSON(resource: CfnFunction): CfnFunction.CodeProperty; + public abstract _toJSON(resource?: cdk.Resource): CfnFunction.CodeProperty; /** * Called when the lambda or layer is initialized to allow this object to @@ -87,7 +89,7 @@ export class S3Code extends Code { this.bucketName = bucket.bucketName; } - public toJSON(_: CfnFunction): CfnFunction.CodeProperty { + public _toJSON(_?: cdk.Resource): CfnFunction.CodeProperty { return { s3Bucket: this.bucketName, s3Key: this.key, @@ -117,7 +119,7 @@ export class InlineCode extends Code { } } - public toJSON(_: CfnFunction): CfnFunction.CodeProperty { + public _toJSON(_?: cdk.Resource): CfnFunction.CodeProperty { return { zipFile: this.code }; @@ -167,9 +169,11 @@ export class AssetCode extends Code { } } - public toJSON(resource: CfnFunction): CfnFunction.CodeProperty { - // https://github.com/awslabs/aws-cdk/issues/1432 - this.asset!.addResourceMetadata(resource, 'Code'); + public _toJSON(resource?: cdk.Resource): CfnFunction.CodeProperty { + if (resource) { + // https://github.com/awslabs/aws-cdk/issues/1432 + this.asset!.addResourceMetadata(resource, 'Code'); + } return { s3Bucket: this.asset!.s3BucketName, diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda.ts b/packages/@aws-cdk/aws-lambda/lib/lambda.ts index 458307d3f6cf6..3bd09a52c14e4 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda.ts @@ -350,7 +350,7 @@ export class Function extends FunctionBase { const resource = new CfnFunction(this, 'Resource', { functionName: props.functionName, description: props.description, - code: new cdk.Token(() => props.code.toJSON(resource)), + code: new cdk.Token(() => props.code._toJSON(resource)), layers: new cdk.Token(() => this.layers.length > 0 ? this.layers.map(layer => layer.layerVersionArn) : undefined), handler: props.handler, timeout: props.timeout, diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 222187816b040..c467a8685159e 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -11,7 +11,7 @@ export interface LayerVersionProps { compatibleRuntimes?: Runtime[]; /** - * The content of this Layer. Using the *inline* code is not permitted. + * The content of this Layer. Using *inline* (per ``code.isInline``) code is not permitted. */ code: Code; @@ -22,6 +22,8 @@ export interface LayerVersionProps { /** * The SPDX licence identifier or URL to the license file for this layer. + * + * @default no license information will be recorded. */ license?: string; @@ -49,10 +51,10 @@ export interface ILayerVersion { * the layer (for example, a CloudFormation changeset execution role) also needs to have the * ``lambda:GetLayerVersion`` permission on the layer version. * + * @param id the ID of the grant in the construct tree. * @param grantee the identification of the grantee. - * @param grantId the ID of the grant in the construct tree. */ - grantUsage(grantee: LayerVersionUsageGrantee, grantId: string): ILayerVersion + grantUsage(id: string, grantee: LayerVersionUsageGrantee): ILayerVersion } /** @@ -62,12 +64,12 @@ export abstract class LayerVersionBase extends cdk.Construct implements ILayerVe public abstract readonly layerVersionArn: string; public abstract readonly compatibleRuntimes?: Runtime[]; - public grantUsage(grantee: LayerVersionUsageGrantee, grantId: string): ILayerVersion { + public grantUsage(id: string, grantee: LayerVersionUsageGrantee): ILayerVersion { if (grantee.organizationId != null && grantee.accountId !== '*') { throw new Error(`OrganizationId can only be specified if AwsAccountId is '*', but it is ${grantee.accountId}`); } - new cdk.Resource(this, grantId, { + new cdk.Resource(this, id, { type: 'AWS::Lambda::LayerVersionPermission', properties: { Action: 'lambda:GetLayerVersion', @@ -142,8 +144,8 @@ export class LayerVersion extends LayerVersionBase { public readonly layerVersionArn: string; public readonly compatibleRuntimes?: Runtime[]; - constructor(parent: cdk.Construct, id: string, props: LayerVersionProps) { - super(parent, id); + constructor(scope: cdk.Construct, id: string, props: LayerVersionProps) { + super(scope, id); if (props.compatibleRuntimes && props.compatibleRuntimes.length === 0) { throw new Error('Attempted to define a Lambda layer that supports no runtime!'); } @@ -157,7 +159,7 @@ export class LayerVersion extends LayerVersionBase { type: 'AWS::Lambda::LayerVersion', properties: { CompatibleRuntimes: props.compatibleRuntimes && props.compatibleRuntimes.map(r => r.name), - Content: props.code.toJSON(), + Content: new cdk.Token(() => props.code._toJSON(resource)), Description: props.description, LayerName: props.name, LicenseInfo: props.license, @@ -206,8 +208,8 @@ export interface SingletonLayerVersionProps extends LayerVersionProps { export class SingletonLayerVersion extends cdk.Construct implements ILayerVersion { private readonly layerVersion: ILayerVersion; - constructor(parent: cdk.Construct, id: string, props: SingletonLayerVersionProps) { - super(parent, id); + constructor(scope: cdk.Construct, id: string, props: SingletonLayerVersionProps) { + super(scope, id); this.layerVersion = this.ensureLayerVersion(props); } @@ -220,15 +222,15 @@ export class SingletonLayerVersion extends cdk.Construct implements ILayerVersio return this.layerVersion.compatibleRuntimes; } - public grantUsage(grantee: LayerVersionUsageGrantee, grantId: string): ILayerVersion { - this.layerVersion.grantUsage(grantee, grantId); + public grantUsage(id: string, grantee: LayerVersionUsageGrantee): ILayerVersion { + this.layerVersion.grantUsage(id, grantee); return this; } private ensureLayerVersion(props: SingletonLayerVersionProps): ILayerVersion { const singletonId = `SingletonLayer-${props.uuid}`; const stack = cdk.Stack.find(this); - const existing = stack.tryFindChild(singletonId); + const existing = stack.node.tryFindChild(singletonId); if (existing) { return existing as unknown as ILayerVersion; } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts index 8bc7fc0cd8b5d..de01eb0c1d5f0 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.ts @@ -7,7 +7,7 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-layer-version-1'); // Just for the example - granting to the current account is not necessary. -const awsAccountId = new cdk.AwsAccountId().toString(); +const awsAccountId = stack.accountId; /// !show const layer = new lambda.LayerVersion(stack, 'MyLayer', { @@ -18,7 +18,7 @@ const layer = new lambda.LayerVersion(stack, 'MyLayer', { }); // To grant usage by other AWS accounts -layer.grantUsage({ accountId: awsAccountId }, 'remote-account-grant'); +layer.grantUsage('remote-account-grant', { accountId: awsAccountId }); // To grant usage to all accounts in some AWS Ogranization // layer.grantUsage({ accountId: '*', organizationId }); diff --git a/packages/@aws-cdk/aws-lambda/test/test.layers.ts b/packages/@aws-cdk/aws-lambda/test/test.layers.ts index 92aaaeef6b888..3629b3c33399d 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.layers.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.layers.ts @@ -19,7 +19,7 @@ export = testCase({ // THEN expect(stack).to(haveResource('AWS::Lambda::LayerVersion', { - Content: cdk.resolve(code.toJSON()), + Content: stack.node.resolve(code._toJSON()), CompatibleRuntimes: ['nodejs8.10'] })); @@ -37,18 +37,18 @@ export = testCase({ }); // WHEN - layer.grantUsage({ accountId: '123456789012' }, 'GrantUsage-123456789012'); - layer.grantUsage({ accountId: '*', organizationId: 'o-123456' }, 'GrantUsage-o-123456'); + layer.grantUsage('GrantUsage-123456789012', { accountId: '123456789012' }); + layer.grantUsage('GrantUsage-o-123456', { accountId: '*', organizationId: 'o-123456' }); // THEN expect(stack).to(haveResource('AWS::Lambda::LayerVersionPermission', { Action: 'lambda:GetLayerVersion', - LayerVersionArn: cdk.resolve(layer.layerVersionArn), + LayerVersionArn: stack.node.resolve(layer.layerVersionArn), Principal: '123456789012', })); expect(stack).to(haveResource('AWS::Lambda::LayerVersionPermission', { Action: 'lambda:GetLayerVersion', - LayerVersionArn: cdk.resolve(layer.layerVersionArn), + LayerVersionArn: stack.node.resolve(layer.layerVersionArn), Principal: '*', OrganizationId: 'o-123456' }));