Skip to content

Commit

Permalink
added support for VPC Lambda functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Chalfant committed Mar 4, 2016
1 parent 3646667 commit a4a0b97
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 18 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ timeout, memory size, and description.
--memory 128 \
--description "ebs backup function"

You can also deploy your Lambda functions to your VPC.

lambder functions new \
--name curl-backend-site \
--bucket my-s3-bucket \
--timeout 30 \
--memory 128 \
--description "curl a site on a private subnet"
--subnet-ids "subnet-1abcdef,subnet-2abcdef"
--security-group-ids "sg-12345678"

Deploy the Lambda function (from within the project directory)

lambder functions deploy
Expand Down
25 changes: 22 additions & 3 deletions lambder/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def __init__(self, config_file):
self.timeout = config['timeout']
self.memory = config['memory']
self.description = config['description']
self.subnet_ids = config['subnet_ids']
self.security_group_ids = config['security_group_ids']

@cli.group()
@click.pass_context
Expand Down Expand Up @@ -102,7 +104,9 @@ def list():
@click.option('--timeout', help='function timeout in seconds')
@click.option('--memory', help='function memory')
@click.option('--description', help='function description')
def new(name, bucket, timeout, memory, description):
@click.option('--subnet-ids', help='comma-separated list of VPC subnet ids')
@click.option('--security-group-ids', help='comma-separated list of VPC security group ids')
def new(name, bucket, timeout, memory, description, subnet_ids, security_group_ids):
""" Create a new lambda project """
config = {}
if timeout:
Expand All @@ -111,6 +115,10 @@ def new(name, bucket, timeout, memory, description):
config['memory'] = memory
if description:
config['description'] = description
if subnet_ids:
config['subnet_ids'] = subnet_ids
if security_group_ids:
config['security_group_ids'] = security_group_ids

lambder.create_project(name, bucket, config)

Expand All @@ -121,18 +129,29 @@ def new(name, bucket, timeout, memory, description):
@click.option('--timeout', help='function timeout in seconds')
@click.option('--memory', help='function memory')
@click.option('--description', help='function description')
@click.option('--subnet-ids', help='comma-separated list of VPC subnet ids')
@click.option('--security-group-ids', help='comma-separated list of VPC security group ids')
@click.pass_obj
def deploy(config, name, bucket, timeout, memory, description):
def deploy(config, name, bucket, timeout, memory, description, subnet_ids, security_group_ids):
""" Deploy/Update a function from a project directory """
# options should override config if it is there
myname = name or config.name
mybucket = bucket or config.bucket
mytimeout = timeout or config.timeout
mymemory = memory or config.memory
mydescription = description or config.description
mysubnet_ids = subnet_ids or config.subnet_ids
mysecurity_group_ids = security_group_ids or config.security_group_ids

vpc_config = {}
if mysubnet_ids and mysecurity_group_ids:
vpc_config = {
'SubnetIds': mysubnet_ids.split(','),
'SecurityGroupIds': mysecurity_group_ids.split(',')
}

click.echo('Deploying {} to {}'.format(myname, mybucket))
lambder.deploy_function(myname, mybucket, mytimeout, mymemory, mydescription)
lambder.deploy_function(myname, mybucket, mytimeout, mymemory, mydescription, vpc_config)

# lambder functions rm
@functions.command()
Expand Down
29 changes: 21 additions & 8 deletions lambder/lambder.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,13 @@ def _put_role_policy(self, role, policy_name, policy_doc):
PolicyDocument=policy_doc
)

def _attach_vpc_policy(self, role):
iam = boto3.client('iam')
iam.attach_role_policy(
RoleName=role,
PolicyArn='arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole'
)

def _lambda_exists(self, name):
awslambda = boto3.client('lambda')
try:
Expand All @@ -273,7 +280,7 @@ def _lambda_exists(self, name):

return True

def _update_lambda(self, name, bucket, key, timeout, memory, description):
def _update_lambda(self, name, bucket, key, timeout, memory, description, vpc_config):
awslambda = boto3.client('lambda')
resp = awslambda.update_function_code(
FunctionName=self._long_name(name),
Expand All @@ -285,10 +292,11 @@ def _update_lambda(self, name, bucket, key, timeout, memory, description):
FunctionName=self._long_name(name),
Timeout=timeout,
MemorySize=memory,
Description=description
Description=description,
VpcConfig=vpc_config
)

def _create_lambda(self, name, bucket, key, role_arn, timeout, memory, description):
def _create_lambda(self, name, bucket, key, role_arn, timeout, memory, description, vpc_config):
awslambda = boto3.client('lambda')
resp = awslambda.create_function(
FunctionName=self._long_name(name),
Expand All @@ -301,7 +309,8 @@ def _create_lambda(self, name, bucket, key, role_arn, timeout, memory, descripti
},
Timeout=timeout,
MemorySize=memory,
Description=description
Description=description,
VpcConfig=vpc_config
)

def _delete_lambda(self, name):
Expand All @@ -323,7 +332,7 @@ def _role_name(self, name):
def _policy_name(self, name):
return self._long_name(name) + 'ExecutePolicy'

def deploy_function(self, name, bucket, timeout, memory, description):
def deploy_function(self, name, bucket, timeout, memory, description, vpc_config):
long_name = self._long_name(name)
s3_key = self._s3_key(name)
role_name = self._role_name(name)
Expand All @@ -337,7 +346,7 @@ def deploy_function(self, name, bucket, timeout, memory, description):
# upload it to s3
self._s3_cp(zfile, bucket, s3_key)

# remote tempfile
# remove tempfile
os.remove(zfile)

# create the lambda execute role if it does not already exist
Expand All @@ -350,13 +359,17 @@ def deploy_function(self, name, bucket, timeout, memory, description):

self._put_role_policy(role, policy_name, policy_doc)

# add the vpc policy to the role if vpc_config is set
if vpc_config:
self._attach_vpc_policy(role_name)

# create or update the lambda function
timeout_i = int(timeout)
if self._lambda_exists(name):
self._update_lambda(name, bucket, s3_key, timeout_i, memory, description)
self._update_lambda(name, bucket, s3_key, timeout_i, memory, description, vpc_config)
else:
time.sleep(5) # wait for role to be created
self._create_lambda(name, bucket, s3_key, role.arn, timeout_i, memory, description)
self._create_lambda(name, bucket, s3_key, role.arn, timeout_i, memory, description, vpc_config)

# List only the lambder functions, i.e. ones starting with 'Lambder-'
def list_functions(self):
Expand Down
22 changes: 18 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
boto3==1.2.3
botocore==1.3.20
awslogs==0.2.0
binaryornot==0.4.0
boto3==1.2.6
botocore==1.4.0
chardet==2.3.0
click==6.2
cookiecutter==1.3.0
docutils==0.12
futures==3.0.4
future==0.15.2
futures==3.0.5
Jinja2==2.8
jmespath==0.9.0
python-dateutil==2.4.2
MarkupSafe==0.23
pipsi==0.9
python-dateutil==2.5.0
ruamel.base==1.0.0
ruamel.ordereddict==0.4.9
ruamel.yaml==0.10.15
six==1.10.0
termcolor==1.1.0
virtualenv==14.0.0
wheel==0.24.0
whichcraft==0.1.1
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

dependencies = [
'click>=6.2',
'boto3>=1.2.3',
'botocore>=1.3.20',
'boto3>=1.2.6',
'botocore>=1.4.0',
'cookiecutter>=1.3.0'
]

setup(
name='lambder',
version='1.1.1',
version='1.2.1',
url='https://github.com/LeafSoftware/python-lambder',
license='MIT',
author='Chris Chalfant',
Expand Down

0 comments on commit a4a0b97

Please sign in to comment.