-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
605 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
Lambda | ||
====== | ||
|
||
.. module:: touchdown.aws.lambda_ | ||
:synopsis: Executing code units on AWS infrastructure without managing servers | ||
|
||
|
||
Function | ||
-------- | ||
|
||
.. class:: Function | ||
|
||
You can register a lambda function against an Amazon account resource:: | ||
|
||
def hello_world(event, context): | ||
print event | ||
|
||
aws.add_lambda_function( | ||
name = 'myfunction', | ||
role = aws.add_role( | ||
name='myrole', | ||
#..... snip ...., | ||
), | ||
code=hello_world, | ||
handler="main.hello_world", | ||
) | ||
|
||
.. attribute:: name | ||
|
||
The name for the function, up to 64 characters. | ||
|
||
.. attribute:: description | ||
|
||
A description for the function. This is shown in the AWS console and API but is not used by lambda itself. | ||
|
||
.. attribute:: role | ||
|
||
A :class:`~touchdown.aws.iam.Role` resource. | ||
|
||
The IAM role that Lambda assumes when it executes your function to access any other Amazon Web Services (AWS) resources. | ||
|
||
.. attribute:: code | ||
|
||
A python callable. For example:: | ||
|
||
def hello_world(event, context): | ||
print event | ||
|
||
aws.add_lambda_function( | ||
name='hello_world', | ||
code=hello_world, | ||
handler='main.hello_world' | ||
... | ||
) | ||
|
||
It must take 2 arguments only - ``event`` and ``context``. | ||
|
||
This is intended for proof of concept demos when first starting out with lambda - there is no mechanism to ship dependencies of this function, it is literally the output of `inspect.getsource()` that is uploaded. | ||
|
||
.. attribute:: code_from_bytes | ||
|
||
.. attribute:: code_from_s3 | ||
|
||
An S3 :py:class:`~touchdown.aws.s3.File`. | ||
|
||
A new version of the lambda function is published when touchdown detects that the date/time stamp of this file is newer than the last modified stamp on the lambda function. | ||
|
||
.. attribute:: handler | ||
|
||
The entry point to call. | ||
|
||
For the ``python2.7`` runtime with a ``shrink_image.py`` module containing a function called ``handler`` the handler would be ``shrink_image.handler``. | ||
|
||
For the ``node`` runtime with a ``CreateThumbnail.js`` module containing an exported function called ``handler``, the handler is ``CreateThumbnail.handler``. | ||
|
||
For the ``java8`` runtime, this would be something like ``package.class-name.handler`` or just ``package.class-name``. | ||
|
||
.. attribute:: timeout | ||
|
||
An integer. The number of seconds (between 1 and 300) that a lambda function is allowed to execute for before it is interrupted. The default is 3 seconds. | ||
|
||
.. attribute:: memory | ||
|
||
The amount of RAM your lambda function is given. The amount of CPU is assigned based on this as well - more RAM means more CPU is allocated. | ||
|
||
The default value is 128mb, which is also the minimum. Can assign up to 1536mb. | ||
|
||
.. attribute:: publish |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,5 @@ Tutorials | |
.. toctree:: | ||
|
||
hello_world | ||
s3_events_with_lambda | ||
django |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
Handling S3 events with lambda functions | ||
======================================== | ||
|
||
Suppose you store incoming media (such as .jpg or .png) in an incoming bucket and want to resize it into an output bucket. In this walkthrough we will use AWS Lambda to perform the transformation automatically - triggered by S3 ``object-created`` events. | ||
|
||
.. warning:: | ||
|
||
This example assumes 2 separate buckets are used. Attempting to use one bucket will result in recursion. | ||
|
||
:: | ||
|
||
aws = workspace.add_aws( | ||
access_key_id='AKI.....A', | ||
secret_access_key='dfsdfsdgrtjhwluy52i3u5ywjedhfkjshdlfjhlkwjhdf', | ||
region='eu-west-1', | ||
) | ||
|
||
resize_role = aws.add_role( | ||
name="resize-role", | ||
policies={ | ||
"logs": { | ||
"Statement": [{ | ||
"Action": [ | ||
"logs:CreateLogGroup", | ||
"logs:CreateLogStream", | ||
"logs:PutLogEvents" | ||
], | ||
"Effect": "Allow", | ||
"Resource": "arn:aws:logs:*:*:*" | ||
}] | ||
} | ||
}, | ||
assume_role_policy={ | ||
"Statement": [{ | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": "lambda.amazonaws.com" | ||
}, | ||
"Action": "sts:AssumeRole" | ||
}], | ||
}, | ||
) | ||
|
||
resize = aws.add_lambda_function( | ||
name="resize-media", | ||
role=role, | ||
handler="main.resize_handler", | ||
) | ||
|
||
incoming = aws.add_bucket( | ||
name="incoming", | ||
notify_lambda=[{ | ||
"name": "resize", | ||
"events": ["s3:ObjectCreated:*"], | ||
"function": resize, | ||
}], | ||
) | ||
|
||
resized = aws.add_bucket( | ||
name="resized", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Copyright 2015 Isotoma Limited | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from .function import Function | ||
from .permission import Permission | ||
from .s3 import S3LambdaNotification | ||
|
||
|
||
__all__ = [ | ||
'Function', | ||
'Permission', | ||
'S3LambdaNotification', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# Copyright 2015 Isotoma Limited | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import base64 | ||
import hashlib | ||
import inspect | ||
import zipfile | ||
from six import StringIO | ||
|
||
from touchdown.core.resource import Resource | ||
from touchdown.core.plan import Plan, Present, XOR | ||
from touchdown.core import argument, serializers | ||
from touchdown.aws.iam import Role | ||
from ..account import BaseAccount | ||
from ..common import SimpleDescribe, SimpleApply, SimpleDestroy | ||
|
||
|
||
class FunctionSerializer(serializers.Formatter): | ||
|
||
MAIN_PY = zipfile.ZipInfo( | ||
'main.py', | ||
date_time=(2004, 7, 6, 0, 0, 0), | ||
) | ||
|
||
def render(self, runner, func): | ||
buf = StringIO() | ||
zf = zipfile.ZipFile( | ||
buf, | ||
mode='w', | ||
compression=zipfile.ZIP_DEFLATED | ||
) | ||
zf.writestr(self.MAIN_PY, inspect.getsource(func)) | ||
zf.close() | ||
return buf.getvalue() | ||
|
||
|
||
class Function(Resource): | ||
|
||
resource_name = "lambda_function" | ||
|
||
name = argument.String(field="FunctionName", min=1, max=140) | ||
description = argument.String(name="Description") | ||
timeout = argument.Integer(name="Timeout", default=3) | ||
runtime = argument.String( | ||
field="Runtime", | ||
default='python2.7', | ||
choices=['nodejs', 'java8', 'python2.7'], | ||
) | ||
handler = argument.String(field="Handler", min=0, max=128) | ||
role = argument.Resource( | ||
Role, | ||
field="Role", | ||
serializer=serializers.Property("Arn"), | ||
) | ||
|
||
code = argument.Function( | ||
field="Code", | ||
serializer=serializers.Dict( | ||
ZipFile=FunctionSerializer(), | ||
) | ||
) | ||
|
||
code_from_bytes = argument.String( | ||
field="Code", | ||
serializer=serializers.Dict( | ||
ZipFile=serializers.String(), | ||
) | ||
) | ||
|
||
code_from_s3 = argument.Resource( | ||
"touchdown.aws.s3.File", | ||
field="Code", | ||
serializer=serializers.Dict( | ||
S3Bucket=serializers.Property("Bucket"), | ||
S3Key=serializers.Property("Key"), | ||
# S3ObjectionVersion=, | ||
) | ||
) | ||
|
||
memory = argument.Integer(name="MemorySize", default=128, min=128, max=1536) | ||
publish = argument.Boolean(name="Publish", default=True) | ||
|
||
account = argument.Resource(BaseAccount) | ||
|
||
|
||
class Describe(SimpleDescribe, Plan): | ||
|
||
resource = Function | ||
service_name = 'lambda' | ||
describe_action = "get_function_configuration" | ||
describe_notfound_exception = "ResourceNotFoundException" | ||
describe_envelope = "@" | ||
key = 'FunctionName' | ||
|
||
|
||
class Apply(SimpleApply, Describe): | ||
|
||
create_action = "create_function" | ||
create_envelope = "@" | ||
update_action = "update_function_configuration" | ||
|
||
signature = ( | ||
Present('name'), | ||
Present('role'), | ||
Present('handler'), | ||
# Runtime must be present - but default is valid | ||
# Present('runtime'), | ||
XOR( | ||
Present('code'), | ||
Present('code_from_bytes'), | ||
Present('code_from_s3'), | ||
), | ||
) | ||
|
||
def update_object(self): | ||
update_code = False | ||
if self.object: | ||
serialized = serializers.Resource().render(self.runner, self.resource) | ||
if 'ZipFile' in serialized['Code']: | ||
hasher = hashlib.sha256(serialized['Code']['ZipFile']) | ||
digest = base64.b64encode(hasher.digest()).decode('utf-8') | ||
if self.object['CodeSha256'] != digest: | ||
update_code = True | ||
elif 'S3Bucket' in serialized['Code']: | ||
f = self.get_plan(self.resource.code_from_s3) | ||
if f.object['LastModified'] > self.object['LastModified']: | ||
update_code = True | ||
|
||
if update_code: | ||
kwargs = { | ||
"FunctionName": self.resource.name, | ||
"Publish": True, | ||
} | ||
kwargs.update(serialized['Code']) | ||
yield self.generic_action( | ||
"Update function code", | ||
self.client.update_function_code, | ||
**kwargs | ||
) | ||
|
||
|
||
class Destroy(SimpleDestroy, Describe): | ||
|
||
destroy_action = "delete_function" |
Oops, something went wrong.