tight-cli

The tight-cli package is one of two components, which together form Tight: the toolset that helps you build event driven applications for serverless runtimes. tight-cli helps you scaffold and maintain Tight apps. This document describes the available commands exposed by tight-cli. For a more thorough discussion of how to use tight-cli to create and manage your application visit the tutorials.

Once installed, you can invoke tight-cli simply by calling tight from the command line:

$ tight

Usage: tight [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  dynamo
  generate
  pip

tight generate

The generate group currently supports two sub-commands: app and function. Use these commands to quickly scaffold your application, functions, and tests.

tight generate app

Usage: tight generate app [OPTIONS] NAME

Options:
  --provider TEXT  Platform providers
  --type TEXT      Provider app type
  --target TEXT    Location where app will be created.
  --help           Show this message and exit.

tight generate app only currently supports the default values for provider and type. Therefore, NAME is really all that is currently required. If you don’t want to generate the app in the current directory, you can override the write target by specifying the --target option.

$ tight generate app my_service
$ cd my_service
$ ls -la

drwxr-xr-x  12 user  group   408B Dec 30 14:40 .
drwxr-xr-x@ 24 user  group   816B Dec 30 14:40 ..
-rw-r--r--   1 user  group   143B Dec 25 17:07 .gitignore
drwxr-xr-x   8 user  group   272B Dec 23 11:22 app
-rw-r--r--   1 user  group    76B Dec 25 16:38 app_index.py
-rw-r--r--   1 user  group   477B Dec 30 14:40 conftest.py
-rw-r--r--   1 user  group    56B Dec 26 02:28 env.dist.yml
-rw-r--r--   1 user  group    60B Dec 17 18:03 requirements-vendor.txt
-rw-r--r--   1 user  group    40B Dec 17 18:05 requirements.txt
drwxr-xr-x   4 user  group   136B Dec 23 12:39 schemas
drwxr-xr-x   4 user  group   136B Dec 23 11:22 tests
-rw-r--r--   1 user  group    55B Dec 30 14:40 tight.yml

tight generate function

Quickly generate a function and test stubs in a Tight app.

Usage: tight generate function [OPTIONS] NAME

Options:
  --provider [aws]       Platform providers
  --type [lambda_proxy]  Function type
  --help                 Show this message and exit.

This command will generate a function module and will also stub integration and unit tests for the generated module:

$ tight generate function my_controller
============================================= test session starts =============================================
platform darwin -- Python 2.7.10, pytest-3.0.5, py-1.4.32, pluggy-0.4.0
rootdir: /Users/michael/Development/my_service, inifile:
collected 2 items

tests/functions/integration/my_controller/test_integration_my_controller.py .
tests/functions/unit/my_controller/test_unit_my_controller.py .

========================================== 2 passed in 0.10 seconds ===========================================
Successfully generated function and tests!

This command generates the following files and directories:

|-app/
|---functions/
|-----my_controller/
|-------handler.py
|-tests/
|---functions/
|-----unit/
|-------my_controller/
|---------test_unit_my_controller.py
|-----integration/
|-------my_controller/
|---------expectations/
|-----------test_get_method.yml
|---------placebos/
|---------test_integration_my_controller.py

The contents of the generated files:

app/functions/my_controller/handler.py

from tight.providers.aws.clients import dynamo_db
import tight.providers.aws.controllers.lambda_proxy_event as lambda_proxy
db = dynamo_db.connect()

@lambda_proxy.get
def get_handler(*args, **kwargs):
    return {
        'statusCode': 200,
        'body': {
            'hello': 'world'
        }
    }

@lambda_proxy.post
def post_handler(*args, **kwargs):
    pass

@lambda_proxy.put
def put_handler(*args, **kwargs):
    pass

@lambda_proxy.patch
def patch_handler(*args, **kwargs):
    pass

@lambda_proxy.options
def options_handler(*args, **kwargs):
    pass

@lambda_proxy.delete
def delete_handler(*args, **kwargs):
    pass

tests/functions/integration/my_controller/test_integration_my_controller.py

import os, json
here = os.path.dirname(os.path.realpath(__file__))
from tight.core.test_helpers import playback, record, expected_response_body

def test_get_method(app, dynamo_db_session):
    playback(__file__, dynamo_db_session, test_get_method.__name__)
    context = {}
    event = {
        'httpMethod': 'GET'
    }
    actual_response = app.my_controller(event, context)
    actual_response_body = json.loads(actual_response['body'])
    expected_response = expected_response_body(here, 'expectations/test_get_method.yml', actual_response)
    assert actual_response['statusCode'] == 200, 'The response statusCode is 200'
    assert actual_response_body == expected_response, 'Expected response body matches the actual response body.'

tests/functions/integration/my_controller/expectations/test_get_method.yml

body: '{"hello":"world"}'
headers: {Access-Control-Allow-Origin: '*'}
statusCode: 200

tests/functions/unit/my_controller/test_unit_my_controller.py

def test_no_boom():
    module = __import__('app.functions.my_controller.handler')
    assert module

tight generate model

Generate a Flywheel model and write to app/models.

Usage: tight generate model [OPTIONS] NAME

Options:
  --help  Show this message and exit.

Example:

$ tight generate model account
$ cd app/models
$ ls
-rw-r--r--  1 user  group   390B Dec 30 15:28 Account.py
-rw-r--r--  1 user  group   460B Dec 23 10:46 __init__.py

The generated model:

from flywheel import Model, Field, Engine
import os

# DynamoDB Model
class Account(Model):
    __metadata__ = {
        '_name': '%s-%s-accounts' % (os.environ['NAME'], os.environ['STAGE']),
        'throughput': {
            'read': 1,
            'write': 1
        }
    }

    id = Field(type=unicode, hash_key=True)

    # Constructor
    def __init__(self, id):
        self.id = id

tight generate env

This command will generate a env.yml file, merging values defined in env.dist.yml and values in the current shell environment.

$ tight generate env
{CI: false, NAME: my-service, STAGE: dev}

tight pip

tight pip is a lightweight command that helps manage dependencies in the context of a Tight app.

tight pip install

Usage: tight pip install [OPTIONS] [PACKAGE_NAME]...

Options:
  --requirements / --no-requirements    Defaults to --no-requirements
  --requirements-file [``CWD``]         Requirements file location
  --target [``tight.yml::vendor_dir``]  Target directory.
  --help                                Show this message and exit.

Typically, after generating an app you’ll want to run tight pip install --requirements from the application root directory. This will install the dependencies to the app/vendored directory and then remove the boto3 and botocore packages; these libraries should not be shipped woth your app since they are provided by AWS in the default Lambda environment.

As you are developing a Tight app, you will undoubtedly need to install additional pip packages. You have two options for installing new dependencies. You can either add the dependency to requirements-vendor.txt and re-run tight pip install --requirements or you can run tight pip install PACKAGE_NAME, which will install the dependencies to app/vendored and then append PACKAGE_NAME to requirements-vendor.txt.

tight dynamo

One of Tight’s primary goals is to make it quick and easy to scaffold RESTful APIs. To help achieve this goal, tight-cli provides a group of commands that helps you manage, run, and test interactions with DynamoDB.

tight dynamo installdb

Run this command to download and expand the latest stable version of DynamoDB. The downloaded tarball will be extracted to the directory dynamo_db.

tight dynamo rundb

This command will run the version of DynamoDB which was downloaded via tight dynamo installdb. This command runs dynamo using a shared database file which is written to dynamo_db/shared-local-instance.db.

This file is deleted on startup if it exsits.

Additionally, this command will traverse the app/models directory and automatically generate tables for models. Models should be instances of Flywheel models.

Before executing this command, you should have run tight generate env or otherwise have defined app/env.yml.

$ tight dynamo rundb

Initializing DynamoDB Local with the following configuration:
Port:       8000
InMemory:   false
DbPath:     ./dynamo_db
SharedDb:   true
shouldDelayTransientStatuses:       false
CorsParams: *

This engine has the following tables [u'my-service-dev-accounts']

As demonstrated in the example above, the command will report on the tables generated from auto-discovered model classes.

tight dynamo generateschema

This command will generate CloudFormation compatible DynamoDB resources from Flywheel models.

Given the following model:

from flywheel import Model, Field, Engine
import os

# DynamoDB Model
class Account(Model):
    __metadata__ = {
        '_name': '%s-%s-accounts' % (os.environ['NAME'], os.environ['STAGE']),
        'throughput': {
            'read': 1,
            'write': 1
        }
    }

    id = Field(type=unicode, hash_key=True)

    # Constructor
    def __init__(self, id):
        self.id = id

Running tight dynamo generateschema will write a YAML file to app/schemas/dynamo:

$ tight dynamo generateschema
$ cd schemas/dynamo
$ ls
-rw-r--r--  1 user  group   265B Dec 30 15:55 accounts.yml

The contents of acounts.yml will be:

Properties:
  AttributeDefinitions:
  - {AttributeName: id, AttributeType: S}
  KeySchema:
  - {AttributeName: id, KeyType: HASH}
  ProvisionedThroughput: {ReadCapacityUnits: 1, WriteCapacityUnits: 1}
  TableName: my-service-dev-accounts
Type: AWS::DynamoDB::Table