mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
Merge pull request #1301 from serverless/schedule
Adde Schedule Event Source support
This commit is contained in:
commit
08d385b966
@ -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
|
||||
- [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
|
||||
- [awsRemove](/docs/plugins/aws/awsRemove.md) - Removes the service with all it's resources from AWS
|
||||
|
||||
13
docs/plugins/aws/awsCompileScheduledEvents.md
Normal file
13
docs/plugins/aws/awsCompileScheduledEvents.md
Normal file
@ -0,0 +1,13 @@
|
||||
# awsCompileScheduledEvents
|
||||
|
||||
This plugins compiles the function schedule event to to a CloudFormation resource.
|
||||
|
||||
## How it works
|
||||
|
||||
`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.
|
||||
|
||||
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.
|
||||
@ -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",
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
const merge = require('lodash').merge;
|
||||
|
||||
class AwsCompileScheduledEvents {
|
||||
constructor(serverless) {
|
||||
this.serverless = serverless;
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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}ScheduleEvent"
|
||||
}]
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const permissionTemplate = `
|
||||
{
|
||||
"Type": "AWS::Lambda::Permission",
|
||||
"Properties": {
|
||||
"FunctionName": { "Fn::GetAtt": ["${functionName}", "Arn"] },
|
||||
"Action": "lambda:InvokeFunction",
|
||||
"Principal": "events.amazonaws.com",
|
||||
"SourceArn": { "Fn::GetAtt": ["${functionName}ScheduleEvent", "Arn"] }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const newScheduleObject = {
|
||||
[`${functionName}ScheduleEvent`]: JSON.parse(scheduleTemplate),
|
||||
};
|
||||
|
||||
const newPermissionObject = {
|
||||
[`${functionName}ScheduleEventPermission`]: JSON.parse(permissionTemplate),
|
||||
};
|
||||
|
||||
merge(this.serverless.service.resources.aws.Resources,
|
||||
newScheduleObject, newPermissionObject);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AwsCompileScheduledEvents;
|
||||
@ -0,0 +1,72 @@
|
||||
'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: {} } };
|
||||
awsCompileScheduledEvents = new AwsCompileScheduledEvents(serverless);
|
||||
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": "helloScheduleEvent"
|
||||
}]
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const permissionResource = `
|
||||
{
|
||||
"Type": "AWS::Lambda::Permission",
|
||||
"Properties": {
|
||||
"FunctionName": { "Fn::GetAtt": ["hello", "Arn"] },
|
||||
"Action": "lambda:InvokeFunction",
|
||||
"Principal": "events.amazonaws.com",
|
||||
"SourceArn": { "Fn::GetAtt": ["helloScheduleEvent", "Arn"] }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
awsCompileScheduledEvents.compileScheduledEvents();
|
||||
|
||||
expect(awsCompileScheduledEvents.serverless.service
|
||||
.resources.aws.Resources.helloScheduleEvent)
|
||||
.to.deep.equal(JSON.parse(scheduleResrouce));
|
||||
expect(awsCompileScheduledEvents.serverless.service
|
||||
.resources.aws.Resources.helloScheduleEventPermission)
|
||||
.to.deep.equal(JSON.parse(permissionResource));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -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
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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'))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user