Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Application Load Balancer event source #6073

Merged
merged 2 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/providers/aws/README.md
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ If you have any questions, [search the forums](https://forum.serverless.com?utm_
<li><a href="./events/schedule.md">Schedule</a></li> <li><a href="./events/schedule.md">Schedule</a></li>
<li><a href="./events/sns.md">SNS</a></li> <li><a href="./events/sns.md">SNS</a></li>
<li><a href="./events/sqs.md">SQS</a></li> <li><a href="./events/sqs.md">SQS</a></li>
<li><a href="./evetns/alb.md">ALB</a></li>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo here. Should be ./events/alb.md.

Copy link
Contributor

@schellack schellack Jun 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I submitted a separate pull request to fix the typo, since this one is already marked as Done.

<li><a href="./events/alexa-skill.md">Alexa Skill</a></li> <li><a href="./events/alexa-skill.md">Alexa Skill</a></li>
<li><a href="./events/alexa-smart-home.md">Alexa Smart Home</a></li> <li><a href="./events/alexa-smart-home.md">Alexa Smart Home</a></li>
<li><a href="./events/iot.md">IoT</a></li> <li><a href="./events/iot.md">IoT</a></li>
Expand Down
32 changes: 32 additions & 0 deletions docs/providers/aws/events/alb.md
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,32 @@
<!--
title: Serverless Framework - AWS Lambda Events - ALB
menuText: Application Load Balancer
menuOrder: 8
description: Setting up AWS Application Load Balancer events with AWS Lambda via the Serverless Framework
layout: Doc
-->

<!-- DOCS-SITE-LINK:START automatically generated -->
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/alb)
<!-- DOCS-SITE-LINK:END -->

# Application Load Balancer

[Application Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) can be used to re-route requests when certain traffic patterns are met. While traffic can be routed to services such as EC2 it [can also be routed to Lambda functions](https://aws.amazon.com/de/blogs/networking-and-content-delivery/lambda-functions-as-targets-for-application-load-balancers/) which can in turn be used process incoming requests.

The Serverless Framework makes it possible to setup the connection between Application Load Balancers and Lamdba functions with the help of the `alb` event.

## Event definition

```yml
functions:
albEventConsumer:
handler: handler.hello
events:
- alb:
listenerArn: arn:aws:elasticloadbalancing:us-east-1:12345:listener/app/my-load-balancer/50dc6c495c0c9188/
priority: 1
conditions:
host: example.com
path: /hello
```
2 changes: 1 addition & 1 deletion docs/providers/aws/events/alexa-skill.md
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- <!--
title: Serverless Framework - AWS Lambda Events - Alexa Skill title: Serverless Framework - AWS Lambda Events - Alexa Skill
menuText: Alexa Skill menuText: Alexa Skill
menuOrder: 8 menuOrder: 9
description: Setting up AWS Alexa Skill Events with AWS Lambda via the Serverless Framework description: Setting up AWS Alexa Skill Events with AWS Lambda via the Serverless Framework
layout: Doc layout: Doc
--> -->
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/events/alexa-smart-home.md
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- <!--
title: Serverless Framework - AWS Lambda Events - Alexa Smart Home title: Serverless Framework - AWS Lambda Events - Alexa Smart Home
menuText: Alexa Smart Home menuText: Alexa Smart Home
menuOrder: 13 menuOrder: 10
description: Setting up AWS Alexa Smart Home Events with AWS Lambda via the Serverless Framework description: Setting up AWS Alexa Smart Home Events with AWS Lambda via the Serverless Framework
layout: Doc layout: Doc
--> -->
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/events/cloudwatch-event.md
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- <!--
title: Serverless Framework - AWS Lambda Events - CloudWatch Event title: Serverless Framework - AWS Lambda Events - CloudWatch Event
menuText: CloudWatch Event menuText: CloudWatch Event
menuOrder: 10 menuOrder: 12
description: Setting up AWS CloudWatch Events with AWS Lambda via the Serverless Framework description: Setting up AWS CloudWatch Events with AWS Lambda via the Serverless Framework
layout: Doc layout: Doc
--> -->
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/events/cloudwatch-log.md
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- <!--
title: Serverless Framework - AWS Lambda Events - CloudWatch Log title: Serverless Framework - AWS Lambda Events - CloudWatch Log
menuText: CloudWatch Log menuText: CloudWatch Log
menuOrder: 11 menuOrder: 13
description: Setting up AWS CloudWatch Logs with AWS Lambda via the Serverless Framework description: Setting up AWS CloudWatch Logs with AWS Lambda via the Serverless Framework
layout: Doc layout: Doc
--> -->
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/events/cognito-user-pool.md
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- <!--
title: Serverless Framework - AWS Lambda Events - Cognito User Pool title: Serverless Framework - AWS Lambda Events - Cognito User Pool
menuText: Cognito User Pool menuText: Cognito User Pool
menuOrder: 12 menuOrder: 14
description: Setting up AWS Cognito User Pool Triggers with AWS Lambda via the Serverless Framework description: Setting up AWS Cognito User Pool Triggers with AWS Lambda via the Serverless Framework
layout: Doc layout: Doc
--> -->
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/events/iot.md
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- <!--
title: Serverless Framework - AWS Lambda Events - IoT title: Serverless Framework - AWS Lambda Events - IoT
menuText: IoT menuText: IoT
menuOrder: 9 menuOrder: 11
description: Setting up AWS IoT Events with AWS Lambda via the Serverless Framework description: Setting up AWS IoT Events with AWS Lambda via the Serverless Framework
layout: Doc layout: Doc
--> -->
Expand Down
6 changes: 6 additions & 0 deletions docs/providers/aws/guide/serverless.yml.md
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ functions:
- cognitoUserPool: - cognitoUserPool:
pool: MyUserPool pool: MyUserPool
trigger: PreSignUp trigger: PreSignUp
- alb:
listenerArn: arn:aws:elasticloadbalancing:us-east-1:12345:listener/app/my-load-balancer/50dc6c495c0c9188/
priority: 1
conditions:
host: example.com
path: /hello


layers: layers:
hello: # A Lambda layer hello: # A Lambda layer
Expand Down
1 change: 1 addition & 0 deletions lib/plugins/Plugins.json
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"./aws/package/compile/events/websockets/index.js", "./aws/package/compile/events/websockets/index.js",
"./aws/package/compile/events/sns/index.js", "./aws/package/compile/events/sns/index.js",
"./aws/package/compile/events/stream/index.js", "./aws/package/compile/events/stream/index.js",
"./aws/package/compile/events/alb/index.js",
"./aws/package/compile/events/alexaSkill/index.js", "./aws/package/compile/events/alexaSkill/index.js",
"./aws/package/compile/events/alexaSmartHome/index.js", "./aws/package/compile/events/alexaSmartHome/index.js",
"./aws/package/compile/events/iot/index.js", "./aws/package/compile/events/iot/index.js",
Expand Down
11 changes: 11 additions & 0 deletions lib/plugins/aws/lib/naming.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -369,6 +369,14 @@ module.exports = {
}`; }`;
}, },


// ALB
getAlbTargetGroupLogicalId(functionName) {
return `${this.getNormalizedFunctionName(functionName)}AlbTargetGroup`;
},
getAlbListenerRuleLogicalId(functionName, idx) {
return `${this.getNormalizedFunctionName(functionName)}AlbListenerRule${idx}`;
},

// Permissions // Permissions
getLambdaS3PermissionLogicalId(functionName, bucketName) { getLambdaS3PermissionLogicalId(functionName, bucketName) {
return `${this.getNormalizedFunctionName(functionName)}LambdaPermission${this return `${this.getNormalizedFunctionName(functionName)}LambdaPermission${this
Expand Down Expand Up @@ -411,4 +419,7 @@ module.exports = {
.getNormalizedFunctionName(functionName)}LambdaPermissionCognitoUserPool${ .getNormalizedFunctionName(functionName)}LambdaPermissionCognitoUserPool${
this.normalizeNameToAlphaNumericOnly(poolId)}TriggerSource${triggerSource}`; this.normalizeNameToAlphaNumericOnly(poolId)}TriggerSource${triggerSource}`;
}, },
getLambdaAlbPermissionLogicalId(functionName) {
return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlb`;
},
}; };
21 changes: 21 additions & 0 deletions lib/plugins/aws/lib/naming.test.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -674,6 +674,13 @@ describe('#naming()', () => {
'CustomMessage' 'CustomMessage'
)).to.equal('FunctionNameLambdaPermissionCognitoUserPoolPool1TriggerSourceCustomMessage'); )).to.equal('FunctionNameLambdaPermissionCognitoUserPoolPool1TriggerSourceCustomMessage');
}); });

describe('#getLambdaAlbPermissionLogicalId()', () => {
it('should normalize the function name', () => {
expect(sdk.naming.getLambdaAlbPermissionLogicalId('functionName'))
.to.equal('FunctionNameLambdaPermissionAlb');
});
});
}); });


describe('#getQueueLogicalId()', () => { describe('#getQueueLogicalId()', () => {
Expand All @@ -682,4 +689,18 @@ describe('#naming()', () => {
.to.equal('FunctionNameEventSourceMappingSQSMyQueue'); .to.equal('FunctionNameEventSourceMappingSQSMyQueue');
}); });
}); });

describe('#getAlbTargetGroupLogicalId()', () => {
it('should normalize the function name', () => {
expect(sdk.naming.getAlbTargetGroupLogicalId('functionName'))
.to.equal('FunctionNameAlbTargetGroup');
});
});

describe('#getAlbListenerRuleLogicalId()', () => {
it('should normalize the function name and add an index', () => {
expect(sdk.naming.getAlbListenerRuleLogicalId('functionName', 0))
.to.equal('FunctionNameAlbListenerRule0');
});
});
}); });
39 changes: 39 additions & 0 deletions lib/plugins/aws/package/compile/events/alb/index.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

const BbPromise = require('bluebird');

const validate = require('./lib/validate');
const compileTargetGroups = require('./lib/targetGroups');
const compileListenerRules = require('./lib/listenerRules');
const compilePermissions = require('./lib/permissions');

class AwsCompileAlbEvents {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.provider = this.serverless.getProvider('aws');

Object.assign(
this,
validate,
compileTargetGroups,
compileListenerRules,
compilePermissions
);

this.hooks = {
'package:compileEvents': () => {
return BbPromise.try(() => {
pmuens marked this conversation as resolved.
Show resolved Hide resolved
this.validated = this.validate();
if (this.validated.events.length === 0) return;

this.compileTargetGroups();
this.compileListenerRules();
this.compilePermissions();
});
},
};
}
}

module.exports = AwsCompileAlbEvents;
83 changes: 83 additions & 0 deletions lib/plugins/aws/package/compile/events/alb/index.test.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,83 @@
'use strict';

const expect = require('chai').expect;
const sinon = require('sinon');
const AwsProvider = require('../../../../provider/awsProvider');
const AwsCompileAlbEvents = require('./index');
const Serverless = require('../../../../../../Serverless');

describe('AwsCompileAlbEvents', () => {
let awsCompileAlbEvents;

beforeEach(() => {
const serverless = new Serverless();
const options = {
stage: 'dev',
region: 'us-east-1',
};
serverless.setProvider('aws', new AwsProvider(serverless, options));
awsCompileAlbEvents = new AwsCompileAlbEvents(serverless, options);
});

describe('#constructor()', () => {
let compileTargetGroupsStub;
let compileListenerRulesStub;
let compilePermissionsStub;

beforeEach(() => {
compileTargetGroupsStub = sinon
.stub(awsCompileAlbEvents, 'compileTargetGroups').resolves();
compileListenerRulesStub = sinon
.stub(awsCompileAlbEvents, 'compileListenerRules').resolves();
compilePermissionsStub = sinon
.stub(awsCompileAlbEvents, 'compilePermissions').resolves();
});

afterEach(() => {
awsCompileAlbEvents.compileTargetGroups.restore();
awsCompileAlbEvents.compileListenerRules.restore();
awsCompileAlbEvents.compilePermissions.restore();
});

it('should have hooks', () => expect(awsCompileAlbEvents.hooks).to.be.not.empty);

it('should set the provider variable to be an instanceof AwsProvider', () =>
expect(awsCompileAlbEvents.provider).to.be.instanceof(AwsProvider));

describe('"package:compileEvents" promise chain', () => {
afterEach(() => {
awsCompileAlbEvents.validate.restore();
});

it('should run the promise chain in order', () => {
const validateStub = sinon
.stub(awsCompileAlbEvents, 'validate').returns({
events: [{
functionName: 'first',
listenerArn: 'arn:aws:elasticloadbalancing:'
+ 'us-east-1:123456789012:listener/app/my-load-balancer/'
+ '50dc6c495c0c9188/f2f7dc8efc522ab2',
priority: 1,
conditions: {
host: 'example.com',
path: '/hello',
},
}],
});

return awsCompileAlbEvents.hooks['package:compileEvents']().then(() => {
expect(validateStub.calledOnce).to.be.equal(true);
expect(compileTargetGroupsStub.calledAfter(validateStub)).to.be.equal(true);
expect(compileListenerRulesStub.calledAfter(compileTargetGroupsStub)).to.be.equal(true);
expect(compilePermissionsStub.calledAfter(compileListenerRulesStub)).to.be.equal(true);
});
});
});

it('should resolve if no functions are given', () => {
awsCompileAlbEvents.serverless.service.functions = {};

return awsCompileAlbEvents.hooks['package:compileEvents']();
});
});
});
44 changes: 44 additions & 0 deletions lib/plugins/aws/package/compile/events/alb/lib/listenerRules.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict';

module.exports = {
compileListenerRules() {
this.validated.events.forEach((event) => {
const listenerRuleLogicalId = this.provider.naming
.getAlbListenerRuleLogicalId(event.functionName, event.priority);
const targetGroupLogicalId = this.provider.naming
.getAlbTargetGroupLogicalId(event.functionName);

const Conditions = [
{
Field: 'path-pattern',
Values: [event.conditions.path],
},
];
if (event.conditions.host) {
Conditions.push({
Field: 'host-header',
Values: [event.conditions.host],
});
}

Object.assign(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
[listenerRuleLogicalId]: {
Type: 'AWS::ElasticLoadBalancingV2::ListenerRule',
Properties: {
Actions: [
{
Type: 'forward',
TargetGroupArn: {
Ref: targetGroupLogicalId,
},
},
],
Conditions,
ListenerArn: event.listenerArn,
Priority: event.priority,
},
},
});
});
},
};