Another WIP commit on the road to an update for the new Lambda API.
Showing
6 changed files
with
78 additions
and
9 deletions
... | @@ -41,12 +41,20 @@ def cli(ctx, config=None, debug=False): | ... | @@ -41,12 +41,20 @@ def cli(ctx, config=None, debug=False): |
41 | @click.pass_context | 41 | @click.pass_context |
42 | def create(ctx): | 42 | def create(ctx): |
43 | context = Context(ctx.obj['config'], ctx.obj['debug']) | 43 | context = Context(ctx.obj['config'], ctx.obj['debug']) |
44 | - click.echo('deploying...') | 44 | + click.echo('creating...') |
45 | context.create() | 45 | context.create() |
46 | click.echo('...done') | 46 | click.echo('...done') |
47 | 47 | ||
48 | @cli.command() | 48 | @cli.command() |
49 | @click.pass_context | 49 | @click.pass_context |
50 | +def update_code(ctx): | ||
51 | + context = Context(ctx.obj['config'], ctx.obj['debug']) | ||
52 | + click.echo('updating code...') | ||
53 | + context.update_code() | ||
54 | + click.echo('...done') | ||
55 | + | ||
56 | +@cli.command() | ||
57 | +@click.pass_context | ||
50 | def invoke(ctx): | 58 | def invoke(ctx): |
51 | context = Context(ctx.obj['config'], ctx.obj['debug']) | 59 | context = Context(ctx.obj['config'], ctx.obj['debug']) |
52 | click.echo('invoking...') | 60 | click.echo('invoking...') |
... | @@ -93,6 +101,7 @@ def status(ctx): | ... | @@ -93,6 +101,7 @@ def status(ctx): |
93 | click.echo(click.style('Event Sources', bold=True)) | 101 | click.echo(click.style('Event Sources', bold=True)) |
94 | if status['event_sources']: | 102 | if status['event_sources']: |
95 | for event_source in status['event_sources']: | 103 | for event_source in status['event_sources']: |
104 | + if event_source: | ||
96 | line = ' {}: {}'.format( | 105 | line = ' {}: {}'.format( |
97 | event_source['EventSourceArn'], event_source['State']) | 106 | event_source['EventSourceArn'], event_source['State']) |
98 | click.echo(click.style(line, fg='green')) | 107 | click.echo(click.style(line, fg='green')) | ... | ... |
... | @@ -13,6 +13,7 @@ | ... | @@ -13,6 +13,7 @@ |
13 | 13 | ||
14 | import logging | 14 | import logging |
15 | import yaml | 15 | import yaml |
16 | +import time | ||
16 | 17 | ||
17 | import kappa.function | 18 | import kappa.function |
18 | import kappa.event_source | 19 | import kappa.event_source |
... | @@ -107,8 +108,12 @@ class Context(object): | ... | @@ -107,8 +108,12 @@ class Context(object): |
107 | self, event_source_cfg)) | 108 | self, event_source_cfg)) |
108 | elif svc == 'sns': | 109 | elif svc == 'sns': |
109 | self.event_sources.append( | 110 | self.event_sources.append( |
110 | - kappa.event_source.SNSEventSource(self, | 111 | + kappa.event_source.SNSEventSource( |
111 | - event_source_cfg)) | 112 | + self, event_source_cfg)) |
113 | + elif svc == 'dynamodb': | ||
114 | + self.event_sources.append( | ||
115 | + kappa.event_source.DynamoDBStreamEventSource( | ||
116 | + self, event_source_cfg)) | ||
112 | else: | 117 | else: |
113 | msg = 'Unknown event source: %s' % event_source_cfg['arn'] | 118 | msg = 'Unknown event source: %s' % event_source_cfg['arn'] |
114 | raise ValueError(msg) | 119 | raise ValueError(msg) |
... | @@ -122,8 +127,16 @@ class Context(object): | ... | @@ -122,8 +127,16 @@ class Context(object): |
122 | self.policy.create() | 127 | self.policy.create() |
123 | if self.role: | 128 | if self.role: |
124 | self.role.create() | 129 | self.role.create() |
130 | + # There is a consistency problem here. | ||
131 | + # If you don't wait for a bit, the function.create call | ||
132 | + # will fail because the policy has not been attached to the role. | ||
133 | + LOG.debug('Waiting for policy/role propogation') | ||
134 | + time.sleep(5) | ||
125 | self.function.create() | 135 | self.function.create() |
126 | 136 | ||
137 | + def update_code(self): | ||
138 | + self.function.update() | ||
139 | + | ||
127 | def invoke(self): | 140 | def invoke(self): |
128 | return self.function.invoke() | 141 | return self.function.invoke() |
129 | 142 | ||
... | @@ -131,13 +144,15 @@ class Context(object): | ... | @@ -131,13 +144,15 @@ class Context(object): |
131 | return self.function.tail() | 144 | return self.function.tail() |
132 | 145 | ||
133 | def delete(self): | 146 | def delete(self): |
134 | - if self.policy: | ||
135 | - self.policy.delete() | ||
136 | - if self.role: | ||
137 | - self.role.delete() | ||
138 | - self.function.delete() | ||
139 | for event_source in self.event_sources: | 147 | for event_source in self.event_sources: |
140 | event_source.remove(self.function) | 148 | event_source.remove(self.function) |
149 | + self.function.delete() | ||
150 | + time.sleep(5) | ||
151 | + if self.role: | ||
152 | + self.role.delete() | ||
153 | + time.sleep(5) | ||
154 | + if self.policy: | ||
155 | + self.policy.delete() | ||
141 | 156 | ||
142 | def status(self): | 157 | def status(self): |
143 | status = {} | 158 | status = {} | ... | ... |
... | @@ -77,7 +77,10 @@ class KinesisEventSource(EventSource): | ... | @@ -77,7 +77,10 @@ class KinesisEventSource(EventSource): |
77 | return response | 77 | return response |
78 | 78 | ||
79 | def status(self, function): | 79 | def status(self, function): |
80 | + response = None | ||
80 | LOG.debug('getting status for event source %s', self.arn) | 81 | LOG.debug('getting status for event source %s', self.arn) |
82 | + uuid = self._get_uuid(function) | ||
83 | + if uuid: | ||
81 | try: | 84 | try: |
82 | response = self._lambda.get_event_source_mapping( | 85 | response = self._lambda.get_event_source_mapping( |
83 | UUID=self._get_uuid(function)) | 86 | UUID=self._get_uuid(function)) |
... | @@ -85,9 +88,16 @@ class KinesisEventSource(EventSource): | ... | @@ -85,9 +88,16 @@ class KinesisEventSource(EventSource): |
85 | except ClientError: | 88 | except ClientError: |
86 | LOG.debug('event source %s does not exist', self.arn) | 89 | LOG.debug('event source %s does not exist', self.arn) |
87 | response = None | 90 | response = None |
91 | + else: | ||
92 | + LOG.debug('No UUID for event source %s', self.arn) | ||
88 | return response | 93 | return response |
89 | 94 | ||
90 | 95 | ||
96 | +class DynamoDBStreamEventSource(KinesisEventSource): | ||
97 | + | ||
98 | + pass | ||
99 | + | ||
100 | + | ||
91 | class S3EventSource(EventSource): | 101 | class S3EventSource(EventSource): |
92 | 102 | ||
93 | def __init__(self, context, config): | 103 | def __init__(self, context, config): | ... | ... |
... | @@ -148,6 +148,7 @@ class Function(object): | ... | @@ -148,6 +148,7 @@ class Function(object): |
148 | self.zip_lambda_function(self.zipfile_name, self.path) | 148 | self.zip_lambda_function(self.zipfile_name, self.path) |
149 | with open(self.zipfile_name, 'rb') as fp: | 149 | with open(self.zipfile_name, 'rb') as fp: |
150 | exec_role = self._context.exec_role_arn | 150 | exec_role = self._context.exec_role_arn |
151 | + LOG.debug('exec_role=%s', exec_role) | ||
151 | try: | 152 | try: |
152 | zipdata = fp.read() | 153 | zipdata = fp.read() |
153 | response = self._lambda_svc.create_function( | 154 | response = self._lambda_svc.create_function( |
... | @@ -164,10 +165,27 @@ class Function(object): | ... | @@ -164,10 +165,27 @@ class Function(object): |
164 | LOG.exception('Unable to upload zip file') | 165 | LOG.exception('Unable to upload zip file') |
165 | self.add_permissions() | 166 | self.add_permissions() |
166 | 167 | ||
168 | + def update(self): | ||
169 | + LOG.debug('updating %s', self.zipfile_name) | ||
170 | + self.zip_lambda_function(self.zipfile_name, self.path) | ||
171 | + with open(self.zipfile_name, 'rb') as fp: | ||
172 | + try: | ||
173 | + zipdata = fp.read() | ||
174 | + response = self._lambda_svc.update_function_code( | ||
175 | + FunctionName=self.name, | ||
176 | + ZipFile=zipdata) | ||
177 | + LOG.debug(response) | ||
178 | + except Exception: | ||
179 | + LOG.exception('Unable to update zip file') | ||
180 | + | ||
167 | def delete(self): | 181 | def delete(self): |
168 | LOG.debug('deleting function %s', self.name) | 182 | LOG.debug('deleting function %s', self.name) |
183 | + response = None | ||
184 | + try: | ||
169 | response = self._lambda_svc.delete_function(FunctionName=self.name) | 185 | response = self._lambda_svc.delete_function(FunctionName=self.name) |
170 | LOG.debug(response) | 186 | LOG.debug(response) |
187 | + except ClientError: | ||
188 | + LOG.debug('function %s: not found', self.name) | ||
171 | return response | 189 | return response |
172 | 190 | ||
173 | def status(self): | 191 | def status(self): | ... | ... |
... | @@ -77,6 +77,8 @@ class Policy(object): | ... | @@ -77,6 +77,8 @@ class Policy(object): |
77 | LOG.exception('Error creating Policy') | 77 | LOG.exception('Error creating Policy') |
78 | 78 | ||
79 | def delete(self): | 79 | def delete(self): |
80 | + response = None | ||
81 | + if self.arn: | ||
80 | LOG.debug('deleting policy %s', self.name) | 82 | LOG.debug('deleting policy %s', self.name) |
81 | response = self._iam_svc.delete_policy(PolicyArn=self.arn) | 83 | response = self._iam_svc.delete_policy(PolicyArn=self.arn) |
82 | LOG.debug(response) | 84 | LOG.debug(response) | ... | ... |
... | @@ -79,13 +79,28 @@ class Role(object): | ... | @@ -79,13 +79,28 @@ class Role(object): |
79 | Path=self.Path, RoleName=self.name, | 79 | Path=self.Path, RoleName=self.name, |
80 | AssumeRolePolicyDocument=AssumeRolePolicyDocument) | 80 | AssumeRolePolicyDocument=AssumeRolePolicyDocument) |
81 | LOG.debug(response) | 81 | LOG.debug(response) |
82 | - except Exception: | 82 | + if self._context.policy: |
83 | + response = self._iam_svc.attach_role_policy( | ||
84 | + RoleName=self.name, | ||
85 | + PolicyArn=self._context.policy.arn) | ||
86 | + LOG.debug(response) | ||
87 | + except ClientError: | ||
83 | LOG.exception('Error creating Role') | 88 | LOG.exception('Error creating Role') |
84 | 89 | ||
85 | def delete(self): | 90 | def delete(self): |
91 | + response = None | ||
86 | LOG.debug('deleting role %s', self.name) | 92 | LOG.debug('deleting role %s', self.name) |
93 | + try: | ||
94 | + LOG.debug('First detach the policy from the role') | ||
95 | + policy_arn = self._context.policy.arn | ||
96 | + if policy_arn: | ||
97 | + response = self._iam_svc.detach_role_policy( | ||
98 | + RoleName=self.name, PolicyArn=policy_arn) | ||
99 | + LOG.debug(response) | ||
87 | response = self._iam_svc.delete_role(RoleName=self.name) | 100 | response = self._iam_svc.delete_role(RoleName=self.name) |
88 | LOG.debug(response) | 101 | LOG.debug(response) |
102 | + except ClientError: | ||
103 | + LOG.exception('role %s not found', self.name) | ||
89 | return response | 104 | return response |
90 | 105 | ||
91 | def status(self): | 106 | def status(self): | ... | ... |
-
Please register or login to post a comment