From f23ae6933f72980698a8d9d47ad1dd4343f46e5c Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 13:00:39 +0200 Subject: [PATCH 1/9] added schedule event --- lib/plugins/Plugins.json | 1 + .../awsCompileScheduledEvents.js | 70 +++++++++++++++++ .../tests/awsCompileScheduledEvents.js | 76 +++++++++++++++++++ lib/templates/serverless.yaml | 2 +- tests/all.js | 1 + tests/integration_test.js | 8 +- 6 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js create mode 100644 lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js diff --git a/lib/plugins/Plugins.json b/lib/plugins/Plugins.json index 298e6e61f..2e44496c4 100644 --- a/lib/plugins/Plugins.json +++ b/lib/plugins/Plugins.json @@ -6,6 +6,7 @@ "./remove/remove.js", "./awsCompileFunctions/awsCompileFunctions.js", "./awsCompileS3Events/awsCompileS3Events.js", + "./awsCompileScheduledEvents/awsCompileScheduledEvents.js", "./awsDeploy/awsDeploy.js", "./awsInvoke/awsInvoke.js", "./awsRemove/awsRemove.js", diff --git a/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js new file mode 100644 index 000000000..85242caa6 --- /dev/null +++ b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js @@ -0,0 +1,70 @@ +'use strict'; + +const merge = require('lodash').merge; + +class AwsCompileScheduledEvents { + constructor(serverless, options) { + this.serverless = serverless; + this.options = options; + + this.hooks = { + 'deploy:compileEvents': this.compileScheduledEvents.bind(this), + }; + } + + compileScheduledEvents() { + if (!this.serverless.service.resources.aws.Resources) { + throw new this.serverless.Error( + 'This plugin needs access to Resources section of the AWS CloudFormation template'); + } + + // iterate over all defined functions + this.serverless.service.getAllFunctions().forEach((functionName) => { + const functionObj = this.serverless.service.getFunction(functionName); + + // checking all three levels in the obj tree + // to avoid "can't read property of undefined" error + if (functionObj.events && functionObj.events.aws && functionObj.events.aws.schedule) { + const scheduleTemplate = ` + { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "${functionObj.events.aws.schedule}", + "State": "ENABLED", + "Targets": [{ + "Arn": { "Fn::GetAtt": ["${functionName}", "Arn"] }, + "Id": "${functionName}Schedule" + }] + } + } + `; + + const permissionTemplate = ` + { + "Type": "AWS::Lambda::Permission", + "Properties": { + "FunctionName": { "Fn::GetAtt": ["${functionName}", "Arn"] }, + "Action": "lambda:InvokeFunction", + "Principal": "events.amazonaws.com", + "SourceArn": { "Fn::GetAtt": ["${functionName}Schedule", "Arn"] } + } + } + `; + + const newScheduleObject = { + [`${functionName}Schedule`]: JSON.parse(scheduleTemplate), + }; + + const newPermissionObject = { + [`${functionName}SchedulePermission`]: JSON.parse(permissionTemplate), + }; + + // merge the new bucket and permission objects into the Resources section + merge(this.serverless.service.resources.aws.Resources, + newScheduleObject, newPermissionObject); + } + }); + } +} + +module.exports = AwsCompileScheduledEvents; diff --git a/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js b/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js new file mode 100644 index 000000000..426cb283b --- /dev/null +++ b/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js @@ -0,0 +1,76 @@ +'use strict'; + +const expect = require('chai').expect; +const AwsCompileScheduledEvents = require('../awsCompileScheduledEvents'); +const Serverless = require('../../../Serverless'); + +describe('awsCompileScheduledEvents', () => { + let serverless; + let awsCompileScheduledEvents; + + beforeEach(() => { + serverless = new Serverless(); + serverless.init(); + serverless.service.resources = { aws: { Resources: {} } }; + const options = { + stage: 'dev', + region: 'us-east-1', + }; + awsCompileScheduledEvents = new AwsCompileScheduledEvents(serverless, options); + awsCompileScheduledEvents.serverless.service.service = 'new-service'; + }); + + describe('#compileScheduledEvents()', () => { + it('should throw an error if the aws resource is not available', () => { + awsCompileScheduledEvents.serverless.service.resources.aws.Resources = false; + expect(() => awsCompileScheduledEvents.compileScheduledEvents()).to.throw(Error); + }); + + it('should compile scheduled events into CF resources', () => { + awsCompileScheduledEvents.serverless.service.functions = { + hello: { + events: { + aws: { + schedule: 'rate(10 minutes)', + }, + }, + }, + }; + + const scheduleResrouce = ` + { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(10 minutes)", + "State": "ENABLED", + "Targets": [{ + "Arn": { "Fn::GetAtt": ["hello", "Arn"] }, + "Id": "helloSchedule" + }] + } + } + `; + + const permissionResource = ` + { + "Type": "AWS::Lambda::Permission", + "Properties": { + "FunctionName": { "Fn::GetAtt": ["hello", "Arn"] }, + "Action": "lambda:InvokeFunction", + "Principal": "events.amazonaws.com", + "SourceArn": { "Fn::GetAtt": ["helloSchedule", "Arn"] } + } + } + `; + + awsCompileScheduledEvents.compileScheduledEvents(); + + expect(awsCompileScheduledEvents.serverless.service + .resources.aws.Resources.helloSchedule) + .to.deep.equal(JSON.parse(scheduleResrouce)); + expect(awsCompileScheduledEvents.serverless.service + .resources.aws.Resources.helloSchedulePermission) + .to.deep.equal(JSON.parse(permissionResource)); + }); + }); +}); diff --git a/lib/templates/serverless.yaml b/lib/templates/serverless.yaml index 887895053..90bd92511 100644 --- a/lib/templates/serverless.yaml +++ b/lib/templates/serverless.yaml @@ -32,7 +32,7 @@ functions: # if this gets too big, you can always use JSON-REF # - first-bucket http_endpoint: post: users/create - scheduled: 5 * * * * + schedule: rate(10 minutes) azure: http_endpoint: direction: in diff --git a/tests/all.js b/tests/all.js index b6950e4fe..c5ca0c1f8 100644 --- a/tests/all.js +++ b/tests/all.js @@ -22,3 +22,4 @@ require('../lib/plugins/awsRemove/tests/all'); require('../lib/plugins/awsInvoke/tests/awsInvoke'); require('../lib/plugins/awsCompileFunctions/tests/awsCompileFunctions'); require('../lib/plugins/awsCompileS3Events/tests/awsCompileS3Events'); +require('../lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents'); diff --git a/tests/integration_test.js b/tests/integration_test.js index 7a0aa5170..08456bf8e 100644 --- a/tests/integration_test.js +++ b/tests/integration_test.js @@ -31,7 +31,7 @@ describe('Service Lifecyle Integration Test', () => { stageName } --region ${ regionName - }`); + }`, { stdio: 'inherit' }); process.chdir(path.join(tmpDir, serviceName)); expect(serverless.utils @@ -50,7 +50,7 @@ describe('Service Lifecyle Integration Test', () => { stageName } --region ${ regionName - }`); + }`, { stdio: 'inherit' }); return CF.describeStacksPromised({ StackName: `${serviceName}-${stageName}` }) .then(d => expect(d.Stacks[0].StackStatus).to.be.equal('UPDATE_COMPLETE')); @@ -83,7 +83,7 @@ describe('Service Lifecyle Integration Test', () => { stageName } --region ${ regionName - }`); + }`, { stdio: 'inherit' }); }); it('should invoke updated function from aws', function () { @@ -103,7 +103,7 @@ describe('Service Lifecyle Integration Test', () => { stageName } --region ${ regionName - }`); + }`, { stdio: 'inherit' }); return CF.describeStacksPromised({ StackName: `${serviceName}-${stageName}` }) .then(d => expect(d.Stacks[0].StackStatus).to.be.equal('DELETE_COMPLETE')) From 11a5507f312de5284e8d607243714bd91c467e25 Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 13:06:42 +0200 Subject: [PATCH 2/9] removed outdated comment --- .../awsCompileScheduledEvents/awsCompileScheduledEvents.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js index 85242caa6..0ce731614 100644 --- a/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js +++ b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js @@ -18,7 +18,6 @@ class AwsCompileScheduledEvents { 'This plugin needs access to Resources section of the AWS CloudFormation template'); } - // iterate over all defined functions this.serverless.service.getAllFunctions().forEach((functionName) => { const functionObj = this.serverless.service.getFunction(functionName); @@ -59,7 +58,6 @@ class AwsCompileScheduledEvents { [`${functionName}SchedulePermission`]: JSON.parse(permissionTemplate), }; - // merge the new bucket and permission objects into the Resources section merge(this.serverless.service.resources.aws.Resources, newScheduleObject, newPermissionObject); } From 4060ad65078a040b1ad64cb0a37f6c3cdf807014 Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 13:33:05 +0200 Subject: [PATCH 3/9] removed options obj --- .../awsCompileScheduledEvents/awsCompileScheduledEvents.js | 3 +-- .../tests/awsCompileScheduledEvents.js | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js index 0ce731614..ecdf297b7 100644 --- a/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js +++ b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js @@ -3,9 +3,8 @@ const merge = require('lodash').merge; class AwsCompileScheduledEvents { - constructor(serverless, options) { + constructor(serverless) { this.serverless = serverless; - this.options = options; this.hooks = { 'deploy:compileEvents': this.compileScheduledEvents.bind(this), diff --git a/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js b/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js index 426cb283b..5bcb6214b 100644 --- a/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js +++ b/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js @@ -12,11 +12,7 @@ describe('awsCompileScheduledEvents', () => { serverless = new Serverless(); serverless.init(); serverless.service.resources = { aws: { Resources: {} } }; - const options = { - stage: 'dev', - region: 'us-east-1', - }; - awsCompileScheduledEvents = new AwsCompileScheduledEvents(serverless, options); + awsCompileScheduledEvents = new AwsCompileScheduledEvents(serverless); awsCompileScheduledEvents.serverless.service.service = 'new-service'; }); From 2c7ae54eac9bf7b57b2376747af2860cc2e132f8 Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 13:41:10 +0200 Subject: [PATCH 4/9] added docs for schedules --- docs/README.md | 2 ++ docs/plugins/aws/awsCompileScheduledEvents.md | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 docs/plugins/aws/awsCompileScheduledEvents.md diff --git a/docs/README.md b/docs/README.md index 21d9c5c33..8b4db9b8a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -27,6 +27,8 @@ Serverless provides (such as service creation, deployment, removal, function inv - AWS plugins - [awsCompileFunctions](/docs/plugins/aws/awsCompileFunctions.md) - Compiles the functions to CloudFormation resources - [awsCompileS3Events](/docs/plugins/aws/awsCompileS3Events.md) - Compiles the S3 events to CloudFormation resources + - [awsCompileS3Events](/docs/plugins/aws/awsCompileScheduledEvents.md) - Compiles the Scheduled events to + CloudFormation resources - [awsDeploy](/docs/plugins/aws/awsDeploy.md) - Deploys the Serverless service to AWS - [awsInvoke](/docs/plugins/aws/awsInvoke.md) - Invokes a AWS lambda function - [awsRemove](/docs/plugins/aws/awsRemove.md) - Removes the service with all it's resources from AWS diff --git a/docs/plugins/aws/awsCompileScheduledEvents.md b/docs/plugins/aws/awsCompileScheduledEvents.md new file mode 100644 index 000000000..552da1042 --- /dev/null +++ b/docs/plugins/aws/awsCompileScheduledEvents.md @@ -0,0 +1,13 @@ +# awsCompileS3Events + +This plugins compiles the function schedule event to to a CloudFormation resource. + +## How it works + +`awsCompileScheduleEvents` hooks into the `compileEvents` hook of the [deploy](/docs/plugins/core/deploy.md) plugin. + +It loops over all functions which are defined in `serverless.yaml`. For each function that has a schedule event defined, a CloudWatch schedule event rule will be created with a status of "enabled" and targeting the lambda function the event is defined within. + +Furthermore a lambda permission for the current function is created which makes is possible to invoke the function at the specified schedule. + +Those two resources are then merged into the `serverless.service.resources.aws.Resources` section. From 9f64281bc3a43cb205d1c9f2124d436cc961d5e7 Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 13:43:59 +0200 Subject: [PATCH 5/9] fix copy typo --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 8b4db9b8a..dd1d645b3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -27,7 +27,7 @@ Serverless provides (such as service creation, deployment, removal, function inv - AWS plugins - [awsCompileFunctions](/docs/plugins/aws/awsCompileFunctions.md) - Compiles the functions to CloudFormation resources - [awsCompileS3Events](/docs/plugins/aws/awsCompileS3Events.md) - Compiles the S3 events to CloudFormation resources - - [awsCompileS3Events](/docs/plugins/aws/awsCompileScheduledEvents.md) - Compiles the Scheduled events to + - [awsCompileScheduledEvents](/docs/plugins/aws/awsCompileScheduledEvents.md) - Compiles the Scheduled events to CloudFormation resources - [awsDeploy](/docs/plugins/aws/awsDeploy.md) - Deploys the Serverless service to AWS - [awsInvoke](/docs/plugins/aws/awsInvoke.md) - Invokes a AWS lambda function From 87ae951335a12a035ad108445fa9082a564af2f4 Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 13:45:30 +0200 Subject: [PATCH 6/9] fixed copy type --- docs/plugins/aws/awsCompileScheduledEvents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/aws/awsCompileScheduledEvents.md b/docs/plugins/aws/awsCompileScheduledEvents.md index 552da1042..e20f54ff8 100644 --- a/docs/plugins/aws/awsCompileScheduledEvents.md +++ b/docs/plugins/aws/awsCompileScheduledEvents.md @@ -1,4 +1,4 @@ -# awsCompileS3Events +# awsCompileScheduledEvents This plugins compiles the function schedule event to to a CloudFormation resource. From 21adc30ded15b2f6b42db10805fb4f69d4d2a764 Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 14:15:42 +0200 Subject: [PATCH 7/9] fix docs --- docs/plugins/aws/awsCompileScheduledEvents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/aws/awsCompileScheduledEvents.md b/docs/plugins/aws/awsCompileScheduledEvents.md index e20f54ff8..b88bdf6af 100644 --- a/docs/plugins/aws/awsCompileScheduledEvents.md +++ b/docs/plugins/aws/awsCompileScheduledEvents.md @@ -4,7 +4,7 @@ This plugins compiles the function schedule event to to a CloudFormation resourc ## How it works -`awsCompileScheduleEvents` hooks into the `compileEvents` hook of the [deploy](/docs/plugins/core/deploy.md) plugin. +`awsCompileScheduledEvents` hooks into the [`deploy:compileEvents`](/docs/plugins/core/deploy.md) hook. It loops over all functions which are defined in `serverless.yaml`. For each function that has a schedule event defined, a CloudWatch schedule event rule will be created with a status of "enabled" and targeting the lambda function the event is defined within. From e56f61718ec14e044bc7ab1aeed78ed1cb5af36c Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 14:18:56 +0200 Subject: [PATCH 8/9] added Event to resource names --- .../awsCompileScheduledEvents.js | 8 ++++---- .../tests/awsCompileScheduledEvents.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js index ecdf297b7..ba8436a6b 100644 --- a/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js +++ b/lib/plugins/awsCompileScheduledEvents/awsCompileScheduledEvents.js @@ -31,7 +31,7 @@ class AwsCompileScheduledEvents { "State": "ENABLED", "Targets": [{ "Arn": { "Fn::GetAtt": ["${functionName}", "Arn"] }, - "Id": "${functionName}Schedule" + "Id": "${functionName}ScheduleEvent" }] } } @@ -44,17 +44,17 @@ class AwsCompileScheduledEvents { "FunctionName": { "Fn::GetAtt": ["${functionName}", "Arn"] }, "Action": "lambda:InvokeFunction", "Principal": "events.amazonaws.com", - "SourceArn": { "Fn::GetAtt": ["${functionName}Schedule", "Arn"] } + "SourceArn": { "Fn::GetAtt": ["${functionName}ScheduleEvent", "Arn"] } } } `; const newScheduleObject = { - [`${functionName}Schedule`]: JSON.parse(scheduleTemplate), + [`${functionName}ScheduleEvent`]: JSON.parse(scheduleTemplate), }; const newPermissionObject = { - [`${functionName}SchedulePermission`]: JSON.parse(permissionTemplate), + [`${functionName}ScheduleEventPermission`]: JSON.parse(permissionTemplate), }; merge(this.serverless.service.resources.aws.Resources, diff --git a/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js b/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js index 5bcb6214b..eccfd554e 100644 --- a/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js +++ b/lib/plugins/awsCompileScheduledEvents/tests/awsCompileScheduledEvents.js @@ -41,7 +41,7 @@ describe('awsCompileScheduledEvents', () => { "State": "ENABLED", "Targets": [{ "Arn": { "Fn::GetAtt": ["hello", "Arn"] }, - "Id": "helloSchedule" + "Id": "helloScheduleEvent" }] } } @@ -54,7 +54,7 @@ describe('awsCompileScheduledEvents', () => { "FunctionName": { "Fn::GetAtt": ["hello", "Arn"] }, "Action": "lambda:InvokeFunction", "Principal": "events.amazonaws.com", - "SourceArn": { "Fn::GetAtt": ["helloSchedule", "Arn"] } + "SourceArn": { "Fn::GetAtt": ["helloScheduleEvent", "Arn"] } } } `; @@ -62,10 +62,10 @@ describe('awsCompileScheduledEvents', () => { awsCompileScheduledEvents.compileScheduledEvents(); expect(awsCompileScheduledEvents.serverless.service - .resources.aws.Resources.helloSchedule) + .resources.aws.Resources.helloScheduleEvent) .to.deep.equal(JSON.parse(scheduleResrouce)); expect(awsCompileScheduledEvents.serverless.service - .resources.aws.Resources.helloSchedulePermission) + .resources.aws.Resources.helloScheduleEventPermission) .to.deep.equal(JSON.parse(permissionResource)); }); }); From bb22303e4c1cbe58c093df000a4154f7d816ecf4 Mon Sep 17 00:00:00 2001 From: "Eslam A. Hefnawy" Date: Fri, 10 Jun 2016 16:00:59 +0200 Subject: [PATCH 9/9] commented out schedule from serverless.yaml --- lib/templates/serverless.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/templates/serverless.yaml b/lib/templates/serverless.yaml index 90bd92511..f8f0d7c1b 100644 --- a/lib/templates/serverless.yaml +++ b/lib/templates/serverless.yaml @@ -32,7 +32,7 @@ functions: # if this gets too big, you can always use JSON-REF # - first-bucket http_endpoint: post: users/create - schedule: rate(10 minutes) + # schedule: rate(10 minutes) azure: http_endpoint: direction: in