Mitch Garnaat

Add the ability to generate the config files based on the environment specified.

......@@ -25,12 +25,13 @@ import kappa.role
LOG = logging.getLogger(__name__)
DebugFmtString = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
InfoFmtString = '\t%(message)s'
InfoFmtString = '...%(message)s'
class Context(object):
def __init__(self, config_file, environment=None, debug=False):
def __init__(self, config_file, environment=None,
debug=False, force=False):
if debug:
self.set_logger('kappa', logging.DEBUG)
else:
......@@ -38,6 +39,7 @@ class Context(object):
self._load_cache()
self.config = yaml.load(config_file)
self.environment = environment
self.force = force
self.policy = kappa.policy.Policy(
self, self.config['environments'][self.environment])
self.role = kappa.role.Role(
......@@ -173,6 +175,9 @@ class Context(object):
def invoke(self):
return self.function.invoke()
def test(self):
return self.function.invoke()
def dryrun(self):
return self.function.dryrun()
......
......@@ -16,6 +16,7 @@ import logging
import os
import zipfile
import time
import shutil
from botocore.exceptions import ClientError
......@@ -122,7 +123,7 @@ class Function(object):
return self._log
def tail(self):
LOG.debug('tailing function: %s', self.name)
LOG.info('tailing function: %s', self.name)
return self.log.tail()
def _zip_lambda_dir(self, zipfile_name, lambda_dir):
......@@ -175,8 +176,17 @@ class Function(object):
except Exception:
LOG.exception('Unable to add permission')
def _copy_config_file(self):
config_name = '{}_config.json'.format(self._context.environment)
config_path = os.path.join(self.path, config_name)
if os.path.exists(config_path):
dest_path = os.path.join(self.path, 'config.json')
LOG.info('copy {} to {}'.format(config_path, dest_path))
shutil.copyfile(config_path, dest_path)
def create(self):
LOG.debug('creating %s', self.zipfile_name)
LOG.info('creating function %s', self.name)
self._copy_config_file()
self.zip_lambda_function(self.zipfile_name, self.path)
with open(self.zipfile_name, 'rb') as fp:
exec_role = self._context.exec_role_arn
......@@ -198,15 +208,26 @@ class Function(object):
LOG.exception('Unable to upload zip file')
self.add_permissions()
def _do_update(self):
do_update = False
if self._context.force:
do_update = True
else:
stats = os.stat(self.zipfile_name)
if self._context.cache.get('zipfile_size') != stats.st_size:
self._context.cache['zipfile_size'] = stats.st_size
do_update = True
return do_update
def update(self):
LOG.debug('updating %s', self.zipfile_name)
self.zip_lambda_function(self.zipfile_name, self.path)
stats = os.stat(self.zipfile_name)
if self._context.cache.get('zipfile_size') != stats.st_size:
self._context.cache['zipfile_size'] = stats.st_size
LOG.info('updating %s', self.name)
self._copy_config_file()
if self._do_update():
self._context.save_cache()
with open(self.zipfile_name, 'rb') as fp:
try:
LOG.info('uploading new function zipfile %s',
self.zipfile_name)
zipdata = fp.read()
response = self._lambda_client.call(
'update_function_code',
......@@ -214,9 +235,9 @@ class Function(object):
ZipFile=zipdata)
LOG.debug(response)
except Exception:
LOG.exception('Unable to update zip file')
LOG.exception('unable to update zip file')
else:
LOG.info('Code has not changed')
LOG.info('function has not changed')
def deploy(self):
if self.exists():
......@@ -224,7 +245,7 @@ class Function(object):
return self.create()
def publish_version(self, description):
LOG.debug('publishing version of %s', self.name)
LOG.info('publishing version of %s', self.name)
try:
response = self._lambda_client.call(
'publish_version',
......@@ -237,7 +258,7 @@ class Function(object):
return response['Version']
def list_versions(self):
LOG.debug('listing versions of %s', self.name)
LOG.info('listing versions of %s', self.name)
try:
response = self._lambda_client.call(
'list_versions_by_function',
......@@ -248,7 +269,7 @@ class Function(object):
return response['Versions']
def create_alias(self, name, description, version=None):
LOG.debug('creating alias of %s', self.name)
LOG.info('creating alias of %s', self.name)
if version is None:
version = self.version
try:
......@@ -263,7 +284,7 @@ class Function(object):
LOG.exception('Unable to create alias')
def list_aliases(self):
LOG.debug('listing aliases of %s', self.name)
LOG.info('listing aliases of %s', self.name)
try:
response = self._lambda_client.call(
'list_aliases',
......@@ -279,7 +300,7 @@ class Function(object):
self.create_alias(name, description, version)
def delete(self):
LOG.debug('deleting function %s', self.name)
LOG.info('deleting function %s', self.name)
response = None
try:
response = self._lambda_client.call(
......@@ -291,7 +312,6 @@ class Function(object):
return response
def status(self):
LOG.debug('getting status for function %s', self.name)
try:
response = self._lambda_client.call(
'get_function',
......
......@@ -121,10 +121,10 @@ class Policy(object):
LOG.exception('Error creating new Policy version')
def deploy(self):
LOG.debug('deploying policy %s', self.name)
LOG.info('deploying policy %s', self.name)
document = self.document()
if not document:
LOG.debug('not a custom policy, no need to create it')
LOG.info('not a custom policy, no need to create it')
return
policy = self.exists()
if policy:
......@@ -138,7 +138,7 @@ class Policy(object):
self._context.save_cache()
self._add_policy_version()
else:
LOG.info('Policy unchanged')
LOG.info('policy unchanged')
else:
# create a new policy
try:
......@@ -157,7 +157,7 @@ class Policy(object):
# This indicates that it was a custom policy created by kappa.
document = self.document()
if self.arn and document:
LOG.debug('deleting policy %s', self.name)
LOG.info('deleting policy %s', self.name)
response = self._iam_client.call(
'delete_policy', PolicyArn=self.arn)
LOG.debug(response)
......
......@@ -35,12 +35,18 @@ from kappa.context import Context
'--environment',
help='Specify which environment to work with'
)
@click.option(
'--force/--no-force',
default=False,
help='Force an update of the Lambda function'
)
@click.pass_context
def cli(ctx, config=None, debug=False, environment=None):
def cli(ctx, config=None, debug=False, environment=None, force=None):
config = config
ctx.obj['debug'] = debug
ctx.obj['config'] = config
ctx.obj['environment'] = environment
ctx.obj['force'] = force
@cli.command()
......@@ -48,10 +54,10 @@ def cli(ctx, config=None, debug=False, environment=None):
def deploy(ctx):
"""Deploy the Lambda function and any policies and roles required"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('deploying...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('deploying')
context.deploy()
click.echo('...done')
click.echo('done')
@cli.command()
......@@ -59,10 +65,10 @@ def deploy(ctx):
def tag(ctx):
"""Deploy the Lambda function and any policies and roles required"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('deploying...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('tagging')
context.deploy()
click.echo('...done')
click.echo('done')
@cli.command()
......@@ -70,13 +76,27 @@ def tag(ctx):
def invoke(ctx):
"""Invoke the command synchronously"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('invoking...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('invoking')
response = context.invoke()
log_data = base64.b64decode(response['LogResult'])
click.echo(log_data)
click.echo(response['Payload'].read())
click.echo('...done')
click.echo('done')
@cli.command()
@click.pass_context
def test(ctx):
"""Test the command synchronously"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'], ctx.obj['force'])
click.echo('testing')
response = context.test()
log_data = base64.b64decode(response['LogResult'])
click.echo(log_data)
click.echo(response['Payload'].read())
click.echo('done')
@cli.command()
......@@ -84,11 +104,11 @@ def invoke(ctx):
def dryrun(ctx):
"""Show you what would happen but don't actually do anything"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('invoking dryrun...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('invoking dryrun')
response = context.dryrun()
click.echo(response)
click.echo('...done')
click.echo('done')
@cli.command()
......@@ -96,11 +116,11 @@ def dryrun(ctx):
def invoke_async(ctx):
"""Invoke the Lambda function asynchronously"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('invoking async...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('invoking async')
response = context.invoke_async()
click.echo(response)
click.echo('...done')
click.echo('done')
@cli.command()
......@@ -108,12 +128,12 @@ def invoke_async(ctx):
def tail(ctx):
"""Show the last 10 lines of the log file"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('tailing logs...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('tailing logs')
for e in context.tail()[-10:]:
ts = datetime.utcfromtimestamp(e['timestamp']//1000).isoformat()
click.echo("{}: {}".format(ts, e['message']))
click.echo('...done')
click.echo('done')
@cli.command()
......@@ -159,10 +179,10 @@ def status(ctx):
def delete(ctx):
"""Delete the Lambda function and related policies and roles"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('deleting...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('deleting')
context.delete()
click.echo('...done')
click.echo('done')
@cli.command()
......@@ -170,10 +190,10 @@ def delete(ctx):
def add_event_sources(ctx):
"""Add any event sources specified in the config file"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('adding event sources...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('adding event sources')
context.add_event_sources()
click.echo('...done')
click.echo('done')
@cli.command()
......@@ -181,10 +201,10 @@ def add_event_sources(ctx):
def update_event_sources(ctx):
"""Update event sources specified in the config file"""
context = Context(ctx.obj['config'], ctx.obj['environment'],
ctx.obj['debug'])
click.echo('updating event sources...')
ctx.obj['debug'], ctx.obj['force'])
click.echo('updating event sources')
context.update_event_sources()
click.echo('...done')
click.echo('done')
cli(obj={})
......