Committed by
GitHub
Merge pull request #69 from coopernurse/cloudwatch-events
CloudWatch events - Fixes #52
Showing
6 changed files
with
157 additions
and
1 deletions
... | @@ -26,6 +26,7 @@ import kappa.event_source.dynamodb_stream | ... | @@ -26,6 +26,7 @@ import kappa.event_source.dynamodb_stream |
26 | import kappa.event_source.kinesis | 26 | import kappa.event_source.kinesis |
27 | import kappa.event_source.s3 | 27 | import kappa.event_source.s3 |
28 | import kappa.event_source.sns | 28 | import kappa.event_source.sns |
29 | +import kappa.event_source.cloudwatch | ||
29 | import kappa.policy | 30 | import kappa.policy |
30 | import kappa.role | 31 | import kappa.role |
31 | import kappa.awsclient | 32 | import kappa.awsclient |
... | @@ -181,6 +182,7 @@ class Context(object): | ... | @@ -181,6 +182,7 @@ class Context(object): |
181 | 'kinesis': kappa.event_source.kinesis.KinesisEventSource, | 182 | 'kinesis': kappa.event_source.kinesis.KinesisEventSource, |
182 | 's3': kappa.event_source.s3.S3EventSource, | 183 | 's3': kappa.event_source.s3.S3EventSource, |
183 | 'sns': kappa.event_source.sns.SNSEventSource, | 184 | 'sns': kappa.event_source.sns.SNSEventSource, |
185 | + 'events': kappa.event_source.cloudwatch.CloudWatchEventSource | ||
184 | } | 186 | } |
185 | for event_source_cfg in event_sources: | 187 | for event_source_cfg in event_sources: |
186 | _, _, svc, _ = event_source_cfg['arn'].split(':', 3) | 188 | _, _, svc, _ = event_source_cfg['arn'].split(':', 3) |
... | @@ -226,7 +228,7 @@ class Context(object): | ... | @@ -226,7 +228,7 @@ class Context(object): |
226 | # There is a consistency problem here. | 228 | # There is a consistency problem here. |
227 | # If you don't wait for a bit, the function.create call | 229 | # If you don't wait for a bit, the function.create call |
228 | # will fail because the policy has not been attached to the role. | 230 | # will fail because the policy has not been attached to the role. |
229 | - LOG.debug('Waiting for policy/role propogation') | 231 | + LOG.debug('Waiting for policy/role propagation') |
230 | time.sleep(5) | 232 | time.sleep(5) |
231 | self.function.create() | 233 | self.function.create() |
232 | self.add_event_sources() | 234 | self.add_event_sources() |
... | @@ -239,6 +241,7 @@ class Context(object): | ... | @@ -239,6 +241,7 @@ class Context(object): |
239 | self.function.deploy() | 241 | self.function.deploy() |
240 | if self.restapi: | 242 | if self.restapi: |
241 | self.restapi.deploy() | 243 | self.restapi.deploy() |
244 | + self.add_event_sources() | ||
242 | 245 | ||
243 | def invoke(self, data): | 246 | def invoke(self, data): |
244 | return self.function.invoke(data) | 247 | return self.function.invoke(data) | ... | ... |
kappa/event_source/cloudwatch.py
0 → 100644
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Copyright (c) 2014, 2015 Mitch Garnaat | ||
3 | +# | ||
4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +# you may not use this file except in compliance with the License. | ||
6 | +# You may obtain a copy of the License at | ||
7 | +# | ||
8 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +# | ||
10 | +# Unless required by applicable law or agreed to in writing, software | ||
11 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +# See the License for the specific language governing permissions and | ||
14 | +# limitations under the License. | ||
15 | + | ||
16 | +import kappa.event_source.base | ||
17 | +import logging | ||
18 | +import uuid | ||
19 | + | ||
20 | +LOG = logging.getLogger(__name__) | ||
21 | + | ||
22 | + | ||
23 | +class CloudWatchEventSource(kappa.event_source.base.EventSource): | ||
24 | + | ||
25 | + def __init__(self, context, config): | ||
26 | + super(CloudWatchEventSource, self).__init__(context, config) | ||
27 | + self._events = kappa.awsclient.create_client('events', context.session) | ||
28 | + self._lambda = kappa.awsclient.create_client('lambda', context.session) | ||
29 | + self._name = config['arn'].split('/')[-1] | ||
30 | + self._context = context | ||
31 | + self._config = config | ||
32 | + | ||
33 | + def get_rule(self): | ||
34 | + response = self._events.call('list_rules', NamePrefix=self._name) | ||
35 | + LOG.debug(response) | ||
36 | + if 'Rules' in response: | ||
37 | + for r in response['Rules']: | ||
38 | + if r['Name'] == self._name: | ||
39 | + return r | ||
40 | + return None | ||
41 | + | ||
42 | + def add(self, function): | ||
43 | + kwargs = { | ||
44 | + 'Name': self._name, | ||
45 | + 'State': 'ENABLED' if self.enabled else 'DISABLED' | ||
46 | + } | ||
47 | + if 'schedule' in self._config: | ||
48 | + kwargs['ScheduleExpression'] = self._config['schedule'] | ||
49 | + if 'pattern' in self._config: | ||
50 | + kwargs['EventPattern'] = self._config['pattern'] | ||
51 | + if 'description' in self._config: | ||
52 | + kwargs['Description'] = self._config['description'] | ||
53 | + if 'role_arn' in self._config: | ||
54 | + kwargs['RoleArn'] = self._config['role_arn'] | ||
55 | + try: | ||
56 | + response = self._events.call('put_rule', **kwargs) | ||
57 | + LOG.debug(response) | ||
58 | + self._config['arn'] = response['RuleArn'] | ||
59 | + response = self._lambda.call('add_permission', | ||
60 | + FunctionName=function.name, | ||
61 | + StatementId=str(uuid.uuid4()), | ||
62 | + Action='lambda:InvokeFunction', | ||
63 | + Principal='events.amazonaws.com', | ||
64 | + SourceArn=response['RuleArn']) | ||
65 | + LOG.debug(response) | ||
66 | + response = self._events.call('put_targets', | ||
67 | + Rule=self._name, | ||
68 | + Targets=[{ | ||
69 | + 'Id': function.name, | ||
70 | + 'Arn': function.arn | ||
71 | + }]) | ||
72 | + LOG.debug(response) | ||
73 | + except Exception: | ||
74 | + LOG.exception('Unable to put CloudWatch event source') | ||
75 | + | ||
76 | + def update(self, function): | ||
77 | + self.add(function) | ||
78 | + | ||
79 | + def remove(self, function): | ||
80 | + LOG.debug('removing CloudWatch event source') | ||
81 | + try: | ||
82 | + rule = self.get_rule() | ||
83 | + if rule: | ||
84 | + response = self._events.call('remove_targets', | ||
85 | + Rule=self._name, | ||
86 | + Ids=[function.name]) | ||
87 | + LOG.debug(response) | ||
88 | + response = self._events.call('delete_rule', | ||
89 | + Name=self._name) | ||
90 | + LOG.debug(response) | ||
91 | + except Exception: | ||
92 | + LOG.exception('Unable to remove CloudWatch event source %s', self._name) | ||
93 | + | ||
94 | + def status(self, function): | ||
95 | + LOG.debug('status for CloudWatch event for %s', function.name) | ||
96 | + return self._to_status(self.get_rule()) | ||
97 | + | ||
98 | + def enable(self, function): | ||
99 | + if self.get_rule(): | ||
100 | + self._events.call('enable_rule', Name=self._name) | ||
101 | + | ||
102 | + def disable(self, function): | ||
103 | + if self.get_rule(): | ||
104 | + self._events.call('disable_rule', Name=self._name) | ||
105 | + | ||
106 | + def _to_status(self, rule): | ||
107 | + if rule: | ||
108 | + return { | ||
109 | + 'EventSourceArn': rule['Arn'], | ||
110 | + 'State': rule['State'] | ||
111 | + } | ||
112 | + return None |
samples/cron/.gitignore
0 → 100644
samples/cron/_src/simple.py
0 → 100644
samples/cron/_tests/test_one.json
0 → 100644
samples/cron/kappa.yml.sample
0 → 100644
1 | +--- | ||
2 | +name: kappa-cron | ||
3 | +environments: | ||
4 | + dev: | ||
5 | + profile: <your profile here> | ||
6 | + region: <your region here> | ||
7 | + policy: | ||
8 | + resources: | ||
9 | + - arn: arn:aws:logs:*:*:* | ||
10 | + actions: | ||
11 | + - "*" | ||
12 | + event_sources: | ||
13 | + - arn: arn:aws:events:<your region here>:<your account id>:rule/kappa-cron-dev | ||
14 | + schedule: rate(1 minute) | ||
15 | + description: cron to run this lambda function every minute | ||
16 | + enabled: true | ||
17 | +lambda: | ||
18 | + description: Kappa sample lambda that runs every minute | ||
19 | + handler: simple.handler | ||
20 | + runtime: python2.7 | ||
21 | + memory_size: 128 | ||
22 | + timeout: 3 | ||
23 | + |
-
Please register or login to post a comment