diff --git a/docs/providers/aws/README.md b/docs/providers/aws/README.md index 77374cacb..a194ca32b 100644 --- a/docs/providers/aws/README.md +++ b/docs/providers/aws/README.md @@ -93,6 +93,7 @@ If you have any questions, [search the forums](https://forum.serverless.com?utm_
  • Schedule
  • SNS
  • SQS
  • +
  • ALB
  • Alexa Skill
  • Alexa Smart Home
  • IoT
  • diff --git a/docs/providers/aws/events/alb.md b/docs/providers/aws/events/alb.md new file mode 100644 index 000000000..001fa3d78 --- /dev/null +++ b/docs/providers/aws/events/alb.md @@ -0,0 +1,47 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/alb) + + +# 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 + +The following code will setup an alb using a HTTPS-based listener. + +```yml +functions: + albEventConsumer: + handler: handler.hello + events: + - alb: + loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 + certificateArn: arn:aws:iam::123456:server-certificate/ProdServerCert + name: alb-handler-https + listener: HTTPS:443 +``` + +The Application Load Balancer `arn` can also be referenced via `Ref`. HTTP listeners don't require the `certificateArn` property to be set. + +```yml +functions: + albEventConsumer: + handler: handler.hello + events: + - alb: + loadBalancerArn: + Ref: MyApplicationLoadBalancer + name: alb-handler-http + listener: HTTP:80 +``` diff --git a/docs/providers/aws/events/alexa-skill.md b/docs/providers/aws/events/alexa-skill.md index b7355ba09..e4e76acc4 100644 --- a/docs/providers/aws/events/alexa-skill.md +++ b/docs/providers/aws/events/alexa-skill.md @@ -1,7 +1,7 @@ diff --git a/docs/providers/aws/events/alexa-smart-home.md b/docs/providers/aws/events/alexa-smart-home.md index a451a4b2e..cff1a86c1 100644 --- a/docs/providers/aws/events/alexa-smart-home.md +++ b/docs/providers/aws/events/alexa-smart-home.md @@ -1,7 +1,7 @@ diff --git a/docs/providers/aws/events/cloudwatch-event.md b/docs/providers/aws/events/cloudwatch-event.md index fc9d32dfb..0236e6f1b 100644 --- a/docs/providers/aws/events/cloudwatch-event.md +++ b/docs/providers/aws/events/cloudwatch-event.md @@ -1,7 +1,7 @@ diff --git a/docs/providers/aws/events/cloudwatch-log.md b/docs/providers/aws/events/cloudwatch-log.md index 7942545f0..629dc5a35 100644 --- a/docs/providers/aws/events/cloudwatch-log.md +++ b/docs/providers/aws/events/cloudwatch-log.md @@ -1,7 +1,7 @@ diff --git a/docs/providers/aws/events/cognito-user-pool.md b/docs/providers/aws/events/cognito-user-pool.md index 0d2662848..a4751364e 100644 --- a/docs/providers/aws/events/cognito-user-pool.md +++ b/docs/providers/aws/events/cognito-user-pool.md @@ -1,7 +1,7 @@ diff --git a/docs/providers/aws/events/iot.md b/docs/providers/aws/events/iot.md index c927ff832..095b9c2a2 100644 --- a/docs/providers/aws/events/iot.md +++ b/docs/providers/aws/events/iot.md @@ -1,7 +1,7 @@ diff --git a/docs/providers/aws/guide/serverless.yml.md b/docs/providers/aws/guide/serverless.yml.md index ff129c57f..ffc517636 100644 --- a/docs/providers/aws/guide/serverless.yml.md +++ b/docs/providers/aws/guide/serverless.yml.md @@ -269,6 +269,11 @@ functions: - cognitoUserPool: pool: MyUserPool trigger: PreSignUp + - alb: + loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 + certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert # only required when HTTPS is used + name: alb-handler-https + listener: HTTPS:443 layers: hello: # A Lambda layer diff --git a/lib/plugins/Plugins.json b/lib/plugins/Plugins.json index 1e63fd66d..253d0a750 100644 --- a/lib/plugins/Plugins.json +++ b/lib/plugins/Plugins.json @@ -38,6 +38,7 @@ "./aws/package/compile/events/websockets/index.js", "./aws/package/compile/events/sns/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/alexaSmartHome/index.js", "./aws/package/compile/events/iot/index.js", diff --git a/lib/plugins/aws/lib/naming.js b/lib/plugins/aws/lib/naming.js index 694fd1021..c99c5af61 100644 --- a/lib/plugins/aws/lib/naming.js +++ b/lib/plugins/aws/lib/naming.js @@ -369,6 +369,14 @@ module.exports = { }`; }, + // ALB + getAlbTargetGroupLogicalId(functionName) { + return `${this.getNormalizedFunctionName(functionName)}AlbTargetGroup`; + }, + getAlbListenerLogicalId(functionName, idx) { + return `${this.getNormalizedFunctionName(functionName)}AlbListener${idx}`; + }, + // Permissions getLambdaS3PermissionLogicalId(functionName, bucketName) { return `${this.getNormalizedFunctionName(functionName)}LambdaPermission${this @@ -411,4 +419,7 @@ module.exports = { .getNormalizedFunctionName(functionName)}LambdaPermissionCognitoUserPool${ this.normalizeNameToAlphaNumericOnly(poolId)}TriggerSource${triggerSource}`; }, + getLambdaAlbPermissionLogicalId(functionName) { + return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlb`; + }, }; diff --git a/lib/plugins/aws/lib/naming.test.js b/lib/plugins/aws/lib/naming.test.js index 1b10d0198..a96536417 100644 --- a/lib/plugins/aws/lib/naming.test.js +++ b/lib/plugins/aws/lib/naming.test.js @@ -674,6 +674,13 @@ describe('#naming()', () => { 'CustomMessage' )).to.equal('FunctionNameLambdaPermissionCognitoUserPoolPool1TriggerSourceCustomMessage'); }); + + describe('#getLambdaAlbPermissionLogicalId()', () => { + it('should normalize the function name', () => { + expect(sdk.naming.getLambdaAlbPermissionLogicalId('functionName')) + .to.equal('FunctionNameLambdaPermissionAlb'); + }); + }); }); describe('#getQueueLogicalId()', () => { @@ -682,4 +689,18 @@ describe('#naming()', () => { .to.equal('FunctionNameEventSourceMappingSQSMyQueue'); }); }); + + describe('#getAlbTargetGroupLogicalId()', () => { + it('should normalize the function name', () => { + expect(sdk.naming.getAlbTargetGroupLogicalId('functionName')) + .to.equal('FunctionNameAlbTargetGroup'); + }); + }); + + describe('#getAlbListenerLogicalId()', () => { + it('should normalize the function name and add an index', () => { + expect(sdk.naming.getAlbListenerLogicalId('functionName', 0)) + .to.equal('FunctionNameAlbListener0'); + }); + }); }); diff --git a/lib/plugins/aws/package/compile/events/alb/index.js b/lib/plugins/aws/package/compile/events/alb/index.js new file mode 100644 index 000000000..00cc1c54a --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/index.js @@ -0,0 +1,41 @@ +'use strict'; + +const BbPromise = require('bluebird'); + +const validate = require('./lib/validate'); +const compileTargetGroups = require('./lib/targetGroups'); +const compileListeners = require('./lib/listeners'); +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, + compileListeners, + compilePermissions + ); + + this.hooks = { + 'package:compileEvents': () => { + this.validated = this.validate(); + + if (this.validated.events.length === 0) { + return BbPromise.resolve(); + } + + return BbPromise.bind(this) + .then(this.compileTargetGroups) + .then(this.compileListeners) + .then(this.compilePermissions); + }, + }; + } +} + +module.exports = AwsCompileAlbEvents; diff --git a/lib/plugins/aws/package/compile/events/alb/index.test.js b/lib/plugins/aws/package/compile/events/alb/index.test.js new file mode 100644 index 000000000..ba1a8b912 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/index.test.js @@ -0,0 +1,81 @@ +'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 compileListenersStub; + let compilePermissionsStub; + + beforeEach(() => { + compileTargetGroupsStub = sinon + .stub(awsCompileAlbEvents, 'compileTargetGroups').resolves(); + compileListenersStub = sinon + .stub(awsCompileAlbEvents, 'compileListeners').resolves(); + compilePermissionsStub = sinon + .stub(awsCompileAlbEvents, 'compilePermissions').resolves(); + }); + + afterEach(() => { + awsCompileAlbEvents.compileTargetGroups.restore(); + awsCompileAlbEvents.compileListeners.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', + listener: 'HTTPS:443', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-1', + }, + ], + }); + + return awsCompileAlbEvents.hooks['package:compileEvents']().then(() => { + expect(validateStub.calledOnce).to.be.equal(true); + expect(compileTargetGroupsStub.calledAfter(validateStub)).to.be.equal(true); + expect(compileListenersStub.calledAfter(compileTargetGroupsStub)).to.be.equal(true); + expect(compilePermissionsStub.calledAfter(compileListenersStub)).to.be.equal(true); + }); + }); + }); + + it('should resolve if no functions are given', () => { + awsCompileAlbEvents.serverless.service.functions = {}; + + return awsCompileAlbEvents.hooks['package:compileEvents'](); + }); + }); +}); diff --git a/lib/plugins/aws/package/compile/events/alb/lib/listeners.js b/lib/plugins/aws/package/compile/events/alb/lib/listeners.js new file mode 100644 index 000000000..a852af554 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/listeners.js @@ -0,0 +1,52 @@ +'use strict'; + +const BbPromise = require('bluebird'); +const _ = require('lodash'); + +module.exports = { + compileListeners() { + this.validated.events.forEach((event, idx) => { + const targetGroupLogicalId = this.provider.naming + .getAlbTargetGroupLogicalId(event.name); + const listenerLogicalId = this.provider.naming + .getAlbListenerLogicalId(event.functionName, idx); + + const listener = event.listener; + const Protocol = listener.split(':')[0].toUpperCase(); + const Port = listener.split(':')[1]; + const LoadBalancerArn = event.loadBalancerArn; + const CertificateArn = event.certificateArn || null; + + let Certificates = []; + if (CertificateArn) { + Certificates = [ + { + CertificateArn, + }, + ]; + } + + _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, { + [listenerLogicalId]: { + Type: 'AWS::ElasticLoadBalancingV2::Listener', + Properties: { + DefaultActions: [ + { + TargetGroupArn: { + Ref: targetGroupLogicalId, + }, + Type: 'forward', + }, + ], + LoadBalancerArn, + Certificates, + Protocol, + Port, + }, + }, + }); + }); + + return BbPromise.resolve(); + }, +}; diff --git a/lib/plugins/aws/package/compile/events/alb/lib/listeners.test.js b/lib/plugins/aws/package/compile/events/alb/lib/listeners.test.js new file mode 100644 index 000000000..f1796b7a6 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/listeners.test.js @@ -0,0 +1,83 @@ +'use strict'; + +const expect = require('chai').expect; +const AwsCompileAlbEvents = require('../index'); +const Serverless = require('../../../../../../../Serverless'); +const AwsProvider = require('../../../../../provider/awsProvider'); + +describe('#compileListeners()', () => { + let awsCompileAlbEvents; + + beforeEach(() => { + const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); + serverless.service.service = 'some-service'; + serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; + + awsCompileAlbEvents = new AwsCompileAlbEvents(serverless); + }); + + it('should create ELB listener resources', () => { + awsCompileAlbEvents.validated = { + events: [ + { + functionName: 'first', + listener: 'HTTPS:443', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-1', + }, + { + functionName: 'second', + listener: 'HTTP:80', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + name: 'some-alb-event-2', + }, + ], + }; + + return awsCompileAlbEvents.compileListeners().then(() => { + const resources = awsCompileAlbEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources; + + expect(resources.FirstAlbListener0).to.deep.equal({ + Type: 'AWS::ElasticLoadBalancingV2::Listener', + Properties: { + Certificates: [ + { + CertificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + }, + ], + DefaultActions: [ + { + TargetGroupArn: { + Ref: 'SomeDashalbDasheventDash1AlbTargetGroup', + }, + Type: 'forward', + }, + ], + LoadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + Port: '443', + Protocol: 'HTTPS', + }, + }); + expect(resources.SecondAlbListener1).to.deep.equal({ + Type: 'AWS::ElasticLoadBalancingV2::Listener', + Properties: { + Certificates: [], + DefaultActions: [ + { + TargetGroupArn: { + Ref: 'SomeDashalbDasheventDash2AlbTargetGroup', + }, + Type: 'forward', + }, + ], + LoadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + Port: '80', + Protocol: 'HTTP', + }, + }); + }); + }); +}); diff --git a/lib/plugins/aws/package/compile/events/alb/lib/permissions.js b/lib/plugins/aws/package/compile/events/alb/lib/permissions.js new file mode 100644 index 000000000..46b6c2426 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/permissions.js @@ -0,0 +1,31 @@ +'use strict'; + +const _ = require('lodash'); +const BbPromise = require('bluebird'); + +module.exports = { + compilePermissions() { + this.validated.events.forEach((event) => { + const lambdaLogicalId = this.provider.naming.getLambdaLogicalId(event.functionName); + + const albPermissionLogicalId = this.provider.naming + .getLambdaAlbPermissionLogicalId(event.functionName); + + _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, { + [albPermissionLogicalId]: { + Type: 'AWS::Lambda::Permission', + Properties: { + FunctionName: { + 'Fn::GetAtt': [lambdaLogicalId, 'Arn'], + }, + Action: 'lambda:InvokeFunction', + Principal: 'elasticloadbalancing.amazonaws.com', + }, + DependsOn: [lambdaLogicalId], + }, + }); + }); + + return BbPromise.resolve(); + }, +}; diff --git a/lib/plugins/aws/package/compile/events/alb/lib/permissions.test.js b/lib/plugins/aws/package/compile/events/alb/lib/permissions.test.js new file mode 100644 index 000000000..82f8d3692 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/permissions.test.js @@ -0,0 +1,74 @@ +'use strict'; + +const expect = require('chai').expect; +const AwsCompileAlbEvents = require('../index'); +const Serverless = require('../../../../../../../Serverless'); +const AwsProvider = require('../../../../../provider/awsProvider'); + +describe('#compilePermissions()', () => { + let awsCompileAlbEvents; + + beforeEach(() => { + const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); + serverless.service.service = 'some-service'; + serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; + + awsCompileAlbEvents = new AwsCompileAlbEvents(serverless); + }); + + it('should create Lambda permission resources', () => { + awsCompileAlbEvents.validated = { + events: [ + { + functionName: 'first', + listener: 'HTTPS:443', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-1', + }, + { + functionName: 'second', + listener: 'HTTP:80', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-2', + }, + ], + }; + + return awsCompileAlbEvents.compilePermissions().then(() => { + const resources = awsCompileAlbEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources; + + expect(resources.FirstLambdaPermissionAlb).to.deep.equal({ + Type: 'AWS::Lambda::Permission', + Properties: { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'FirstLambdaFunction', + 'Arn', + ], + }, + Principal: 'elasticloadbalancing.amazonaws.com', + }, + DependsOn: ['FirstLambdaFunction'], + }); + expect(resources.SecondLambdaPermissionAlb).to.deep.equal({ + Type: 'AWS::Lambda::Permission', + Properties: { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'SecondLambdaFunction', + 'Arn', + ], + }, + Principal: 'elasticloadbalancing.amazonaws.com', + }, + DependsOn: ['SecondLambdaFunction'], + }); + }); + }); +}); diff --git a/lib/plugins/aws/package/compile/events/alb/lib/targetGroups.js b/lib/plugins/aws/package/compile/events/alb/lib/targetGroups.js new file mode 100644 index 000000000..3f85da001 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/targetGroups.js @@ -0,0 +1,36 @@ +'use strict'; + +const BbPromise = require('bluebird'); +const _ = require('lodash'); + +module.exports = { + compileTargetGroups() { + this.validated.events.forEach((event) => { + const targetGroupLogicalId = this.provider.naming + .getAlbTargetGroupLogicalId(event.name); + const lambdaLogicalId = this.provider.naming + .getLambdaLogicalId(event.functionName); + const lambdaPermissionLogicalId = this.provider.naming + .getLambdaAlbPermissionLogicalId(event.functionName); + + _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, { + [targetGroupLogicalId]: { + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup', + Properties: { + TargetType: 'lambda', + Targets: [ + { + Id: { + 'Fn::GetAtt': [lambdaLogicalId, 'Arn'], + }, + }, + ], + }, + DependsOn: [lambdaPermissionLogicalId], + }, + }); + }); + + return BbPromise.resolve(); + }, +}; diff --git a/lib/plugins/aws/package/compile/events/alb/lib/targetGroups.test.js b/lib/plugins/aws/package/compile/events/alb/lib/targetGroups.test.js new file mode 100644 index 000000000..dd9b28237 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/targetGroups.test.js @@ -0,0 +1,80 @@ +'use strict'; + +const expect = require('chai').expect; +const AwsCompileAlbEvents = require('../index'); +const Serverless = require('../../../../../../../Serverless'); +const AwsProvider = require('../../../../../provider/awsProvider'); + +describe('#compileTargetGroups()', () => { + let awsCompileAlbEvents; + + beforeEach(() => { + const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); + serverless.service.service = 'some-service'; + serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; + + awsCompileAlbEvents = new AwsCompileAlbEvents(serverless); + }); + + it('should create ELB target group resources', () => { + awsCompileAlbEvents.validated = { + events: [ + { + functionName: 'first', + listener: 'HTTPS:443', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-1', + }, + { + functionName: 'second', + listener: 'HTTP:80', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-2', + }, + ], + }; + + return awsCompileAlbEvents.compileTargetGroups().then(() => { + const resources = awsCompileAlbEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources; + + expect(resources.SomeDashalbDasheventDash1AlbTargetGroup).to.deep.equal({ + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup', + Properties: { + TargetType: 'lambda', + Targets: [ + { + Id: { + 'Fn::GetAtt': [ + 'FirstLambdaFunction', + 'Arn', + ], + }, + }, + ], + }, + DependsOn: ['FirstLambdaPermissionAlb'], + }); + expect(resources.SomeDashalbDasheventDash2AlbTargetGroup).to.deep.equal({ + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup', + Properties: { + TargetType: 'lambda', + Targets: [ + { + Id: { + 'Fn::GetAtt': [ + 'SecondLambdaFunction', + 'Arn', + ], + }, + }, + ], + }, + DependsOn: ['SecondLambdaPermissionAlb'], + }); + }); + }); +}); diff --git a/lib/plugins/aws/package/compile/events/alb/lib/validate.js b/lib/plugins/aws/package/compile/events/alb/lib/validate.js new file mode 100644 index 000000000..e3102edb1 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/validate.js @@ -0,0 +1,31 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = { + validate() { + const events = []; + + _.forEach(this.serverless.service.functions, (functionObject, functionName) => { + _.forEach(functionObject.events, (event) => { + if (_.has(event, 'alb')) { + if (_.isObject(event.alb)) { + const albObj = { + name: event.alb.name, + loadBalancerArn: event.alb.loadBalancerArn, + certificateArn: event.alb.certificateArn, + listener: event.alb.listener, + // the following is data which is not defined on the event-level + functionName, + }; + events.push(albObj); + } + } + }); + }); + + return { + events, + }; + }, +}; diff --git a/lib/plugins/aws/package/compile/events/alb/lib/validate.test.js b/lib/plugins/aws/package/compile/events/alb/lib/validate.test.js new file mode 100644 index 000000000..8be6969f5 --- /dev/null +++ b/lib/plugins/aws/package/compile/events/alb/lib/validate.test.js @@ -0,0 +1,67 @@ +'use strict'; + +const expect = require('chai').expect; +const AwsCompileAlbEvents = require('../index'); +const Serverless = require('../../../../../../../Serverless'); +const AwsProvider = require('../../../../../provider/awsProvider'); + +describe('#validate()', () => { + let awsCompileAlbEvents; + + beforeEach(() => { + const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); + serverless.service.service = 'some-service'; + serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; + + awsCompileAlbEvents = new AwsCompileAlbEvents(serverless); + }); + + it('should detect alb event definitions', () => { + awsCompileAlbEvents.serverless.service.functions = { + first: { + events: [ + { + alb: { + listener: 'HTTPS:443', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-1', + }, + }, + ], + }, + second: { + events: [ + { + alb: { + listener: 'HTTP:80', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-2', + }, + }, + ], + }, + }; + + const validated = awsCompileAlbEvents.validate(); + + expect(validated.events).to.deep.equal([ + { + functionName: 'first', + listener: 'HTTPS:443', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-1', + }, + { + functionName: 'second', + listener: 'HTTP:80', + loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456:loadbalancer/app/my-load-balancer/50dc6c495c0c9188', // eslint-disable-line + certificateArn: 'arn:aws:iam::123456:server-certificate/ProdServerCert', + name: 'some-alb-event-2', + }, + ]); + }); +}); diff --git a/lib/plugins/create/templates/aws-clojure-gradle/serverless.yml b/lib/plugins/create/templates/aws-clojure-gradle/serverless.yml index b167892d0..3dd357308 100644 --- a/lib/plugins/create/templates/aws-clojure-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-clojure-gradle/serverless.yml @@ -90,6 +90,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-clojurescript-gradle/serverless.yml b/lib/plugins/create/templates/aws-clojurescript-gradle/serverless.yml index a297fee2a..993def7e6 100644 --- a/lib/plugins/create/templates/aws-clojurescript-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-clojurescript-gradle/serverless.yml @@ -99,6 +99,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-csharp/serverless.yml b/lib/plugins/create/templates/aws-csharp/serverless.yml index 81c9930c5..48ce3064f 100644 --- a/lib/plugins/create/templates/aws-csharp/serverless.yml +++ b/lib/plugins/create/templates/aws-csharp/serverless.yml @@ -90,6 +90,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-fsharp/serverless.yml b/lib/plugins/create/templates/aws-fsharp/serverless.yml index 2c304a440..4d2757657 100644 --- a/lib/plugins/create/templates/aws-fsharp/serverless.yml +++ b/lib/plugins/create/templates/aws-fsharp/serverless.yml @@ -87,6 +87,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-go-dep/serverless.yml b/lib/plugins/create/templates/aws-go-dep/serverless.yml index b36eca0f8..b8ba76d50 100644 --- a/lib/plugins/create/templates/aws-go-dep/serverless.yml +++ b/lib/plugins/create/templates/aws-go-dep/serverless.yml @@ -98,6 +98,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-go-mod/serverless.yml b/lib/plugins/create/templates/aws-go-mod/serverless.yml index 7cc8abe00..65082379d 100644 --- a/lib/plugins/create/templates/aws-go-mod/serverless.yml +++ b/lib/plugins/create/templates/aws-go-mod/serverless.yml @@ -98,6 +98,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-go/serverless.yml b/lib/plugins/create/templates/aws-go/serverless.yml index fe0c0492a..9ee090fdb 100644 --- a/lib/plugins/create/templates/aws-go/serverless.yml +++ b/lib/plugins/create/templates/aws-go/serverless.yml @@ -98,6 +98,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml b/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml index 27bbd89ba..105257433 100644 --- a/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml @@ -84,6 +84,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-java-gradle/serverless.yml b/lib/plugins/create/templates/aws-java-gradle/serverless.yml index faff3364a..4d9e011c2 100644 --- a/lib/plugins/create/templates/aws-java-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-java-gradle/serverless.yml @@ -84,6 +84,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-java-maven/serverless.yml b/lib/plugins/create/templates/aws-java-maven/serverless.yml index 807128ade..c39589916 100644 --- a/lib/plugins/create/templates/aws-java-maven/serverless.yml +++ b/lib/plugins/create/templates/aws-java-maven/serverless.yml @@ -84,6 +84,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml b/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml index 92bb8538f..973f28286 100644 --- a/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml @@ -84,6 +84,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml b/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml index 3702074f7..12e4b9fd4 100644 --- a/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml @@ -84,6 +84,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml index a2c045481..e59789ad2 100644 --- a/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml @@ -80,6 +80,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-nodejs/serverless.yml b/lib/plugins/create/templates/aws-nodejs/serverless.yml index cb2f09e97..15f12f583 100644 --- a/lib/plugins/create/templates/aws-nodejs/serverless.yml +++ b/lib/plugins/create/templates/aws-nodejs/serverless.yml @@ -89,6 +89,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-provided/serverless.yml b/lib/plugins/create/templates/aws-provided/serverless.yml index 90922dea2..757c86e78 100644 --- a/lib/plugins/create/templates/aws-provided/serverless.yml +++ b/lib/plugins/create/templates/aws-provided/serverless.yml @@ -89,6 +89,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-python/serverless.yml b/lib/plugins/create/templates/aws-python/serverless.yml index d77ff1625..7c9111f3f 100644 --- a/lib/plugins/create/templates/aws-python/serverless.yml +++ b/lib/plugins/create/templates/aws-python/serverless.yml @@ -89,6 +89,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-python3/serverless.yml b/lib/plugins/create/templates/aws-python3/serverless.yml index da05941bb..f2e656d84 100644 --- a/lib/plugins/create/templates/aws-python3/serverless.yml +++ b/lib/plugins/create/templates/aws-python3/serverless.yml @@ -89,6 +89,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-ruby/serverless.yml b/lib/plugins/create/templates/aws-ruby/serverless.yml index de18084b9..079186796 100644 --- a/lib/plugins/create/templates/aws-ruby/serverless.yml +++ b/lib/plugins/create/templates/aws-ruby/serverless.yml @@ -89,6 +89,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: diff --git a/lib/plugins/create/templates/aws-scala-sbt/serverless.yml b/lib/plugins/create/templates/aws-scala-sbt/serverless.yml index 1b4684faa..d737b96d6 100644 --- a/lib/plugins/create/templates/aws-scala-sbt/serverless.yml +++ b/lib/plugins/create/templates/aws-scala-sbt/serverless.yml @@ -86,6 +86,11 @@ functions: # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp +# - alb: +# loadBalancerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:loadbalancer/app/my-load-balancer/50dc6c495c0c9188 +# certificateArn: arn:aws:iam::XXXXXX:server-certificate/ProdServerCert +# name: alb-handler-https +# listener: HTTPS:443 # Define function environment variables here # environment: