diff --git a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts index 431b9bf6a71d9..c096730b1e8eb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts @@ -1,11 +1,14 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; +import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { Function as LambdaFunction, FunctionProps } from './function'; +import { Function as LambdaFunction, FunctionProps, EnvironmentOptions } from './function'; import { FunctionBase } from './function-base'; import { Version } from './lambda-version'; +import { ILayerVersion } from './layers'; import { Permission } from './permission'; +import { Runtime } from './runtime'; /** * Properties for a newly created singleton Lambda @@ -47,6 +50,12 @@ export class SingletonFunction extends FunctionBase { public readonly functionArn: string; public readonly role?: iam.IRole; public readonly permissionsNode: cdk.ConstructNode; + + /** + * The runtime environment for the Lambda function. + */ + public readonly runtime: Runtime; + protected readonly canCreatePermissions: boolean; private lambdaFunction: LambdaFunction; @@ -59,6 +68,7 @@ export class SingletonFunction extends FunctionBase { this.functionArn = this.lambdaFunction.functionArn; this.functionName = this.lambdaFunction.functionName; this.role = this.lambdaFunction.role; + this.runtime = this.lambdaFunction.runtime; this.grantPrincipal = this.lambdaFunction.grantPrincipal; this.canCreatePermissions = true; // Doesn't matter, addPermission is overriden anyway @@ -78,6 +88,20 @@ export class SingletonFunction extends FunctionBase { return this.lambdaFunction.connections; } + /** + * The LogGroup where the Lambda function's logs are made available. + * + * If either `logRetention` is set or this property is called, a CloudFormation custom resource is added to the stack that + * pre-creates the log group as part of the stack deployment, if it already doesn't exist, and sets the correct log retention + * period (never expire, by default). + * + * Further, if the log group already exists and the `logRetention` is not set, the custom resource will reset the log retention + * to never expire even if it was configured with a different value. + */ + public get logGroup(): logs.ILogGroup { + return this.lambdaFunction.logGroup; + } + /** * Returns a `lambda.Version` which represents the current version of this * singleton Lambda function. A new version will be created every time the @@ -90,6 +114,28 @@ export class SingletonFunction extends FunctionBase { return this.lambdaFunction.currentVersion; } + /** + * Adds an environment variable to this Lambda function. + * If this is a ref to a Lambda function, this operation results in a no-op. + * @param key The environment variable key. + * @param value The environment variable's value. + * @param options Environment variable options. + */ + public addEnvironment(key: string, value: string, options?: EnvironmentOptions) { + return this.lambdaFunction.addEnvironment(key, value, options); + } + + /** + * Adds one or more Lambda Layers to this Lambda function. + * + * @param layers the layers to be added. + * + * @throws if there are already 5 layers on this function, or the layer is incompatible with this function's runtime. + */ + public addLayers(...layers: ILayerVersion[]) { + return this.lambdaFunction.addLayers(...layers); + } + public addPermission(name: string, permission: Permission) { return this.lambdaFunction.addPermission(name, permission); } diff --git a/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts b/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts index 3bf78253d5ed6..4200b7a18a6e5 100644 --- a/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts @@ -2,6 +2,7 @@ import '@aws-cdk/assert-internal/jest'; import { ResourcePart } from '@aws-cdk/assert-internal'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; +import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; import * as lambda from '../lib'; @@ -109,6 +110,57 @@ describe('singleton lambda', () => { }, ResourcePart.CompleteDefinition); }); + test('Environment is added to Lambda, when .addEnvironment() is provided one key pair', () => { + // GIVEN + const stack = new cdk.Stack(); + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + + // WHEN + singleton.addEnvironment('KEY', 'value'); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::Function', { + Environment: { + Variables: { + KEY: 'value', + }, + }, + }); + }); + + test('Layer is added to Lambda, when .addLayers() is provided a valid layer', () => { + // GIVEN + const stack = new cdk.Stack(); + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + const bucket = new s3.Bucket(stack, 'Bucket'); + const layer = new lambda.LayerVersion(stack, 'myLayer', { + code: new lambda.S3Code(bucket, 'ObjectKey'), + compatibleRuntimes: [lambda.Runtime.PYTHON_2_7], + }); + + // WHEN + singleton.addLayers(layer); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::Function', { + Layers: [{ + Ref: 'myLayerBA1B098A', + }], + }); + }); + test('grantInvoke works correctly', () => { // GIVEN const stack = new cdk.Stack(); @@ -154,6 +206,41 @@ describe('singleton lambda', () => { .toThrow(/contains environment variables .* and is not compatible with Lambda@Edge/); }); + test('logGroup is correctly returned', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + + // THEN + expect(singleton.logGroup.logGroupName).toBeDefined(); + expect(singleton.logGroup.logGroupArn).toBeDefined(); + }); + + test('runtime is correctly returned', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.PYTHON_2_7, + handler: 'index.hello', + timeout: cdk.Duration.minutes(5), + }); + + // THEN + expect(singleton.runtime).toStrictEqual(lambda.Runtime.PYTHON_2_7); + }); + test('current version of a singleton function', () => { // GIVEN const stack = new cdk.Stack();