Skip to content

Commit

Permalink
Merge pull request #14 from garnaat/update-for-ga
Browse files Browse the repository at this point in the history
Update for ga
  • Loading branch information
garnaat committed Apr 28, 2015
2 parents 3670fb0 + 904ea37 commit cb38fe5
Show file tree
Hide file tree
Showing 25 changed files with 875 additions and 335 deletions.
49 changes: 31 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,52 @@ There are quite a few steps involved in developing a Lambda function.
You have to:

* Write the function itself (Javascript only for now)
* Create the IAM roles required by the Lambda function itself (the executing
role) as well as the policy required by whoever is invoking the Lambda
function (the invocation role)
* Create the IAM role required by the Lambda function itself (the executing
role) to allow it access to any resources it needs to do its job
* Add additional permissions to the Lambda function if it is going to be used
in a Push model (e.g. S3, SNS) rather than a Pull model.
* Zip the function and any dependencies and upload it to AWS Lambda
* Test the function with mock data
* Retrieve the output of the function from CloudWatch Logs
* Add an event source to the function
* View the output of the live function

Kappa tries to help you with some of this. The IAM roles are created
in a CloudFormation template and kappa takes care of creating, updating, and
deleting the CloudFormation stack. Kappa will also zip up the function and
Kappa tries to help you with some of this. It allows you to create an IAM
managed policy or use an existing one. It creates the IAM execution role for
you and associates the policy with it. Kappa will zip up the function and
any dependencies and upload them to AWS Lambda. It also sends test data
to the uploaded function and finds the related CloudWatch log stream and
displays the log events. Finally, it will add the event source to turn
your function on.

If you need to make changes, kappa will allow you to easily update your Lambda
function with new code or update your event sources as needed.

Getting Started
---------------

Kappa is a command line tool. The basic command format is:

kappa <path to config file> <command> [optional command args]

Where ``command`` is one of:

* deploy - deploy the CloudFormation template containing the IAM roles and zip
the function and upload it to AWS Lambda
* test - send test data to the new Lambda function
* create - creates the IAM policy (if necessary), the IAM role, and zips and
uploads the Lambda function code to the Lambda service
* invoke - make a synchronous call to your Lambda function, passing test data
and display the resulting log data
* invoke_async - make an asynchronous call to your Lambda function passing test
data.
* dryrun - make the call but only check things like permissions and report
back. Don't actually run the code.
* tail - display the most recent log events for the function (remember that it
can take several minutes before log events are available from CloudWatch)
* add-event-sources - hook up an event source to your Lambda function
* delete - delete the CloudFormation stack containing the IAM roles and delete
the Lambda function
* delete - delete the Lambda function, remove any event sources, delete the IAM
policy and role
* update_code - Upload new code for your Lambda function
* update_event_sources - Update the event sources based on the information in
your kappa config file
* status - display summary information about functions, stacks, and event
sources related to your project.

Expand All @@ -58,14 +73,12 @@ An example project based on a Kinesis stream can be found in
The basic workflow is:

* Create your Lambda function
* Create your CloudFormation template with the execution and invocation roles
* Create any custom IAM policy you need to execute your Lambda function
* Create some sample data
* Create the YAML config file with all of the information
* Run ``kappa <path-to-config> deploy`` to create roles and upload function
* Run ``kappa <path-to-config> test`` to invoke the function with test data
* Run ``kappa <path-to-config> tail`` to view the functions output in CloudWatch logs
* Run ``kappa <path-to-config> create`` to create roles and upload function
* Run ``kappa <path-to-config> invoke`` to invoke the function with test data
* Run ``kappa <path-to-config> update_code`` to upload new code for your Lambda
function
* Run ``kappa <path-to-config> add-event-source`` to hook your function up to the event source
* Run ``kappa <path-to-config> tail`` to see more output

If you have to make changes in your function or in your IAM roles, simply run
``kappa deploy`` again and the changes will be uploaded as necessary.
83 changes: 61 additions & 22 deletions bin/kappa
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# language governing permissions and limitations under the License.
from datetime import datetime
import logging
import base64

import click

Expand All @@ -38,18 +39,46 @@ def cli(ctx, config=None, debug=False):

@cli.command()
@click.pass_context
def deploy(ctx):
def create(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('deploying...')
context.deploy()
click.echo('creating...')
context.create()
click.echo('...done')

@cli.command()
@click.pass_context
def test(ctx):
def update_code(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('testing...')
context.test()
click.echo('updating code...')
context.update_code()
click.echo('...done')

@cli.command()
@click.pass_context
def invoke(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('invoking...')
response = context.invoke()
log_data = base64.b64decode(response['LogResult'])
click.echo(log_data)
click.echo('...done')

@cli.command()
@click.pass_context
def dryrun(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('invoking dryrun...')
response = context.dryrun()
click.echo(response)
click.echo('...done')

@cli.command()
@click.pass_context
def invoke_async(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('invoking async...')
response = context.invoke_async()
click.echo(response)
click.echo('...done')

@cli.command()
Expand All @@ -67,33 +96,35 @@ def tail(ctx):
def status(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
status = context.status()
click.echo(click.style('Stack', bold=True))
if status['stack']:
for stack in status['stack']['Stacks']:
line = ' {}: {}'.format(stack['StackId'], stack['StackStatus'])
click.echo(click.style(line, fg='green'))
else:
click.echo(click.style(' None', fg='green'))
click.echo(click.style('Policy', bold=True))
if status['policy']:
line = ' {} ({})'.format(
status['policy']['PolicyName'],
status['policy']['Arn'])
click.echo(click.style(line, fg='green'))
click.echo(click.style('Role', bold=True))
if status['role']:
line = ' {} ({})'.format(
status['role']['Role']['RoleName'],
status['role']['Role']['Arn'])
click.echo(click.style(line, fg='green'))
click.echo(click.style('Function', bold=True))
if status['function']:
line = ' {}'.format(
status['function']['Configuration']['FunctionName'])
line = ' {} ({})'.format(
status['function']['Configuration']['FunctionName'],
status['function']['Configuration']['FunctionArn'])
click.echo(click.style(line, fg='green'))
else:
click.echo(click.style(' None', fg='green'))
click.echo(click.style('Event Sources', bold=True))
if status['event_sources']:
for event_source in status['event_sources']:
if 'EventSource' in event_source:
if event_source:
line = ' {}: {}'.format(
event_source['EventSource'], event_source['IsActive'])
event_source['EventSourceArn'], event_source['State'])
click.echo(click.style(line, fg='green'))
else:
line = ' {}'.format(
event_source['CloudFunctionConfiguration']['Id'])
click.echo(click.style(line, fg='green'))
else:
click.echo(click.style(' None', fg='green'))
click.echo(click.style(' None', fg='green'))

@cli.command()
@click.pass_context
Expand All @@ -111,6 +142,14 @@ def add_event_sources(ctx):
context.add_event_sources()
click.echo('...done')

@cli.command()
@click.pass_context
def update_event_sources(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('updating event sources...')
context.update_event_sources()
click.echo('...done')


if __name__ == '__main__':
cli(obj={})
15 changes: 7 additions & 8 deletions kappa/aws.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2014 Mitch Garnaat http://garnaat.org/
# Copyright (c) 2014,2015 Mitch Garnaat http://garnaat.org/
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
Expand All @@ -11,21 +11,20 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

import botocore.session
import boto3


class __AWS(object):

def __init__(self, profile=None, region=None):
def __init__(self, profile_name=None, region_name=None):
self._client_cache = {}
self._session = botocore.session.get_session()
self._session.profile = profile
self._region = region
self._session = boto3.session.Session(
region_name=region_name, profile_name=profile_name)

def create_client(self, client_name):
if client_name not in self._client_cache:
self._client_cache[client_name] = self._session.create_client(
client_name, self._region)
self._client_cache[client_name] = self._session.client(
client_name)
return self._client_cache[client_name]


Expand Down
Loading

0 comments on commit cb38fe5

Please sign in to comment.