Mitch Garnaat

Merge pull request #10 from garnaat/add-status-command

Some refactoring.  Added a status command.  Rewrote the CLI to take more...
......@@ -18,47 +18,96 @@ import click
from kappa.context import Context
@click.command()
@click.option(
'--config',
help="Path to the Kappa config YAML file",
@click.group()
@click.argument(
'config',
type=click.File('rb'),
envvar='KAPPA_CONFIG',
default=None
)
@click.option(
'--debug/--no-debug',
default=False,
help='Turn on debugging output'
)
@click.argument(
'command',
required=True,
type=click.Choice(['deploy', 'test', 'tail', 'add-event-sources', 'delete'])
)
def main(config=None, debug=False, command=None):
ctx = Context(config, debug)
if command == 'deploy':
click.echo('Deploying ...')
ctx.deploy()
click.echo('...done')
elif command == 'test':
click.echo('Sending test data ...')
ctx.test()
click.echo('...done')
elif command == 'tail':
events = ctx.tail()
for event in events:
print(event['message'])
elif command == 'delete':
click.echo('Deleting ...')
ctx.delete()
click.echo('...done')
elif command == 'add-event-sources':
click.echo('Adding event sources ...')
ctx.add_event_sources()
click.echo('...done')
@click.pass_context
def cli(ctx, config=None, debug=False):
config = config
ctx.obj['debug'] = debug
ctx.obj['config'] = config
@cli.command()
@click.pass_context
def deploy(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('deploying...')
context.deploy()
click.echo('...done')
@cli.command()
@click.pass_context
def test(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('testing...')
context.test()
click.echo('...done')
@cli.command()
@click.pass_context
def tail(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('tailing logs...')
context.tail()
click.echo('...done')
@cli.command()
@click.pass_context
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('Function', bold=True))
if status['function']:
line = ' {}'.format(
status['function']['Configuration']['FunctionName'])
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:
line = ' {}: {}'.format(
event_source['EventSource'], event_source['IsActive'])
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'))
@cli.command()
@click.pass_context
def delete(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('deleting...')
context.delete()
click.echo('...done')
@cli.command()
@click.pass_context
def add_event_sources(ctx):
context = Context(ctx.obj['config'], ctx.obj['debug'])
click.echo('adding event sources...')
context.add_event_sources()
click.echo('...done')
if __name__ == '__main__':
main()
cli(obj={})
......
......@@ -108,10 +108,7 @@ class Context(object):
event_source.add(self.function)
def deploy(self):
if self._stack.exists():
self._stack.update()
else:
self._stack.create()
self._stack.update()
self.function.upload()
def test(self):
......@@ -123,3 +120,14 @@ class Context(object):
def delete(self):
self._stack.delete()
self.function.delete()
for event_source in self.event_sources:
event_source.remove(self.function)
def status(self):
status = {}
status['stack'] = self._stack.status()
status['function'] = self.function.status()
status['event_sources'] = []
for event_source in self.event_sources:
status['event_sources'].append(event_source.status(self.function))
return status
......
......@@ -13,6 +13,8 @@
import logging
from botocore.exceptions import ClientError
import kappa.aws
LOG = logging.getLogger(__name__)
......@@ -70,6 +72,17 @@ class KinesisEventSource(EventSource):
LOG.debug(response)
return response
def status(self, function):
LOG.debug('getting status for event source %s', self.arn)
try:
response = self._lambda.get_event_source(
UUID=self._get_uuid(function))
LOG.debug(response)
except ClientError:
LOG.debug('event source %s does not exist', self.arn)
response = None
return response
class S3EventSource(EventSource):
......@@ -112,3 +125,12 @@ class S3EventSource(EventSource):
Bucket=self._get_bucket_name(),
NotificationConfiguration=response)
LOG.debug(response)
def status(self, function):
LOG.debug('status for s3 notification for %s', function.name)
response = self._s3.get_bucket_notification(
Bucket=self._get_bucket_name())
LOG.debug(response)
if 'CloudFunctionConfiguration' not in response:
response = None
return response
......
......@@ -15,6 +15,8 @@ import logging
import os
import zipfile
from botocore.exceptions import ClientError
import kappa.aws
import kappa.log
......@@ -148,6 +150,17 @@ class Function(object):
LOG.debug(response)
return response
def status(self):
LOG.debug('getting status for function %s', self.name)
try:
response = self._lambda_svc.get_function(
FunctionName=self.name)
LOG.debug(response)
except ClientError:
LOG.debug('function %s not found', self.name)
response = None
return response
def invoke_asynch(self, data_file):
LOG.debug('_invoke_async %s', data_file)
with open(data_file) as fp:
......
......@@ -98,7 +98,7 @@ class Stack(object):
msg = 'Could not create stack %s: %s' % (self.name, status)
raise ValueError(msg)
def create(self):
def _create(self):
LOG.debug('create_stack: stack_name=%s', self.name)
template_body = open(self.template_path).read()
try:
......@@ -110,7 +110,7 @@ class Stack(object):
LOG.exception('Unable to create stack')
self.wait()
def update(self):
def _update(self):
LOG.debug('create_stack: stack_name=%s', self.name)
template_body = open(self.template_path).read()
try:
......@@ -125,6 +125,15 @@ class Stack(object):
LOG.exception('Unable to update stack')
self.wait()
def update(self):
if self.exists():
self._update()
else:
self._create()
def status(self):
return self.exists()
def delete(self):
LOG.debug('delete_stack: stack_name=%s', self.name)
try:
......
botocore==0.82.0
botocore==0.94.0
click==3.3
PyYAML>=3.11
mock>=1.0.1
......
......@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
import os
requires = [
'botocore==0.82.0',
'botocore==0.94.0',
'click==3.3',
'PyYAML>=3.11'
]
......
......@@ -56,10 +56,10 @@ class TestStack(unittest.TestCase):
stack = Stack(mock_context, Config)
self.assertTrue(stack.exists())
def test_create(self):
def test_update(self):
mock_context = mock.Mock()
stack = Stack(mock_context, Config)
stack.create()
stack.update()
def test_delete(self):
mock_context = mock.Mock()
......