Jose Diaz-Gonzalez
Committed by GitHub

Merge pull request #69 from coopernurse/cloudwatch-events

CloudWatch events - Fixes #52
...@@ -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)
......
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
1 +.kappa/
2 +kappa.yml
3 +*.zip
4 +
1 +import logging
2 +import time
3 +
4 +LOG = logging.getLogger()
5 +LOG.setLevel(logging.DEBUG)
6 +
7 +
8 +def handler(event, context):
9 + LOG.debug(event)
10 + return {'status': 'success', 'time': time.time()}
1 +{
2 + "foo": "bar",
3 + "fie": "baz"
4 +}
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 +