Skip to content

Commit 80ddeb1

Browse files
authored
Merge pull request #14 from HyperBrain/issue-#7-event-support
Fixes #7 event support
2 parents fa0871e + 2d9e5e1 commit 80ddeb1

File tree

3 files changed

+119
-2
lines changed

3 files changed

+119
-2
lines changed

README.md

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ A valid use is to forward the alias name as environment variable to the lambdas
7272
and use it there for tagging of log messages. Then you see immediately which
7373
aliased lambda is the origin.
7474

75-
Any other use is strongly discouraged.
75+
Any other use with the further exception of lambda event subscriptions (see below)
76+
is strongly discouraged.
7677

7778
## Log groups (not yet finished)
7879

@@ -104,6 +105,85 @@ that they have to update their configuration too - most likely, they updated it
104105
already, because normally you rebase or merge your upstream and get the changes
105106
automatically.
106107

108+
## Event subscriptions
109+
110+
Event subscriptions that are defined for a lambda function will be deployed per
111+
alias, i.e. the event will trigger the correct deployed aliased function.
112+
113+
### Use with global resources
114+
115+
Event subscriptions can reference resources that are available throughout all
116+
aliases if they reference the same resource id. That means that an event will
117+
trigger all aliases that are deployed with the subscription defined.
118+
119+
Example:
120+
121+
```
122+
functions:
123+
testfct1:
124+
description: 'My test function'
125+
handler: handlers/testfct1/handler.handle
126+
events:
127+
- stream:
128+
type: kinesis
129+
arn: "arn:aws:kinesis:${self:provider.region}:XXXXXX:stream/my-kinesis"
130+
- http:
131+
method: GET
132+
path: /func1
133+
resources:
134+
Resources:
135+
myKinesis:
136+
Type: AWS::Kinesis::Stream
137+
Properties:
138+
Name: my-kinesis
139+
ShardCount: 1
140+
```
141+
142+
When a function is deployed to an alias it will now also listen to the *my-kinesis*
143+
stream events. This is useful, if you want to test new implementations with an
144+
existing resource.
145+
146+
### Use with per alias resources
147+
148+
There might be cases where you want to test with your private resources first,
149+
before you deploy changes to the master alias. Or you just want to create separate
150+
resources and event subscriptions per alias.
151+
152+
The solution here is to make the resource id dependent on the alias name, so that
153+
the alias effectively owns the resource and the event subscription is bound to it.
154+
155+
Example:
156+
157+
```
158+
functions:
159+
testfct1:
160+
description: 'My test function'
161+
handler: handlers/testfct1/handler.handle
162+
events:
163+
- stream:
164+
type: kinesis
165+
arn: "arn:aws:kinesis:${self:provider.region}:XXXXXX:stream/my-kinesis-${self.provider.alias}"
166+
- http:
167+
method: GET
168+
path: /func1
169+
resources:
170+
Resources:
171+
myKinesis${self:provider.alias}:
172+
Type: AWS::Kinesis::Stream
173+
Properties:
174+
Name: my-kinesis-${self.provider.alias}
175+
ShardCount: 1
176+
```
177+
178+
### Named streams
179+
180+
The examples above use named streams. I know that this is not perfect as changes
181+
that require replacement are not possible. The reason for the named resources is,
182+
that Serverless currently only supports event arns that are strings.
183+
The change is already in the pipeline there. Afterwards you just can reference
184+
the event arns with CF functions like "Fn::GetAtt" or "Ref". I will update
185+
the examples as soon as it is fixed there and publicly available.
186+
107187
## Serverless info integration
108188

109189
The plugin integrates with the Serverless info command. It will extend the information

lib/aliasRestructureStack.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
const BbPromise = require('bluebird');
1313
const _ = require('lodash');
14+
const utils = require('./utils');
1415

1516
/**
1617
* Merge template definitions that are still in use into the new template
@@ -419,6 +420,37 @@ module.exports = {
419420
return BbPromise.resolve([ currentTemplate, aliasStackTemplates ]);
420421
},
421422

423+
aliasHandleEvents(currentTemplate, aliasStackTemplates) {
424+
425+
const stageStack = this._serverless.service.provider.compiledCloudFormationTemplate;
426+
const aliasStack = this._serverless.service.provider.compiledCloudFormationAliasTemplate;
427+
428+
const subscriptions = _.assign({}, _.pickBy(_.get(stageStack, 'Resources', {}), [ 'Type', 'AWS::Lambda::EventSourceMapping' ]));
429+
430+
_.forOwn(subscriptions, (subscription, name) => {
431+
// Reference alias as FunctionName
432+
const functionNameRef = utils.findAllReferences(_.get(subscription, 'Properties.FunctionName'));
433+
const functionName = _.get(functionNameRef, '[0].ref', '').replace(/LambdaFunction$/, '');
434+
if (_.isEmpty(functionName)) {
435+
// FIXME: Can this happen at all?
436+
this._serverless.cli.log(`Strange thing: No function name defined for ${name}`);
437+
return;
438+
}
439+
440+
subscription.Properties.FunctionName = { Ref: `${functionName}Alias` };
441+
subscription.DependsOn = [ `${functionName}Alias` ];
442+
443+
// Remove mapping from stage stack
444+
delete stageStack.Resources[name];
445+
});
446+
447+
// Move event subscriptions to alias stack
448+
_.defaults(aliasStack.Resources, subscriptions);
449+
450+
// Forward inputs to the promise chain
451+
return BbPromise.resolve([ currentTemplate, aliasStackTemplates ]);
452+
},
453+
422454
aliasRestructureStack(currentTemplate, aliasStackTemplates) {
423455

424456
this._serverless.cli.log('Preparing aliase ...');
@@ -432,6 +464,7 @@ module.exports = {
432464
.spread(this.aliasHandleLambdaRole)
433465
.spread(this.aliasHandleFunctions)
434466
.spread(this.aliasHandleApiGateway)
467+
.spread(this.aliasHandleEvents)
435468
.then(() => BbPromise.resolve());
436469
}
437470

lib/removeAlias.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ module.exports = {
111111
const obsoleteRefs = _.concat(obsoleteFuncResources, obsoleteResources);
112112

113113
// Remove all obsolete resource references from the IAM policy statements
114+
const emptyStatements = [];
114115
const statementResources = utils.findReferences(currentRolePolicyStatements, obsoleteRefs);
115116
_.forEach(statementResources, resourcePath => {
116117
const indices = /.*?\[([0-9]+)\].*?\[([0-9]+)\]/.exec(resourcePath);
@@ -119,9 +120,12 @@ module.exports = {
119120
const resourceIndex = indices[2];
120121

121122
_.pullAt(currentRolePolicyStatements[statementIndex].Resource, resourceIndex);
122-
_.pull(currentRolePolicyStatements[statementIndex], statement => _.isEmpty(statement.Resource));
123+
if (_.isEmpty(currentRolePolicyStatements[statementIndex].Resource)) {
124+
emptyStatements.push(statementIndex);
125+
}
123126
}
124127
});
128+
_.pullAt(currentRolePolicyStatements, emptyStatements);
125129

126130
// Set references to obsoleted resources in fct env to "REMOVED" in case
127131
// the alias that is removed was the last deployment of the stage.

0 commit comments

Comments
 (0)