Painless AWS Chalice application debug
By: Alex Gelman
Tags: Chalice, Serverless, Debugging, AWS, AWS Lambda

Photo by Per Lööv on Unsplash

When you’re developing serverless applications using Function-as-a-Service services like AWS lambda, there comes a time when you need to troubleshoot your application. Doing so in a cloud environment can prove difficult since you cannot simply attach a debugger to your code running in the cloud.

You may have tried to debug your serverless functions by adding print statements, but I’m going to show you how it’s possible to run and debug local serverless functions written using AWS’s Chalice micro-framework.

Additionally you’ll see how to create integration tests that run your code locally, while integrating with cloud resources. This approach will save you time and money, since you won’t need to re-upload your functions on every change.

Getting Started with a Chalice Application

AWS Chalice is a Python micro-framework, designed to provide developers with a familiar flask-like experience when developing serverless applications.

Before we can get started, you’ll need the following prerequisites:

Python 3.7.5 https://www.python.org/downloads/release/python-375/

PyCharm IDE https://www.jetbrains.com/pycharm/download/

Chalice CLI https://chalice.readthedocs.io/en/latest/quickstart.html

Postman https://www.getpostman.com/downloads/

Let’s start with a simple Chalice application that will allow us to show how we can troubleshoot it locally:

Open a terminal and navigate to the folder you’d like to create the project in.

Run the command:

chalice new-project

The on-screen prompt will ask you for a project name, enter troubleshooting-chalice as the project name.

You should see a new directory with the name troubleshooting-chalice , navigate into the new directory

Figure 1 — Creating a new project using Chalice CLI

Create a new python 3.7.5 virtual environment by running

pipenv --python 3.7.5

Activate the virtual environment by running

pipenv shell

Now install chalice in the virtual environment by running

pipenv install chalice

Open the directory in PyCharm, you should see the following file structure:

Figure 2- Basic Chalice project structure

The file app.py contains a simple hello world lambda function.

The . chalice directory contains configurations of the way the application is deployed in AWS.

Open the app.py file and look at the index method

@app.route('/')

Def index():

return{'hello':'world'}

This method represents a Hello World Lambda function that will be triggered via API gateway calls to the base route ‘/’

Now that you have a simple application, let’s see how you can debug and test it locally before deploying to AWS.

Debugging Locally

You can start the application locally by running the command chalice local , this will start a local web server running your application.

Use Postman to invoke the API locally by sending an HTTP GET request to the address http://localhost:8000/.

Figure 3 — Using Postman to execute the hello world function

You should see the response containing ‘hello world’.

The local server supports hot reload, whenever you make changes to the code and save the file the server will reload to serve the latest code.

To attach a debugger, you need to stop it from automatically reloading code changes.

Stop the running server by pressing ctrl+c in the terminal window to stop the process.

Next, restart the server without the hot reload feature by running the command:

chalice local — no-autoreload

Now back in PyCharm go to the Run menu and choose the option Attach to Process… .

A new menu will open, the menu lists all python processes.

Select the chalice local process to attach a debugger to it.

Figure 4 — PyCharm attach to process menu

Now place a breakpoint in the index() method.

Invoke the API again using Postman.

The execution will stop at the breakpoint, you’ll be able to inspect the local variables, step through the code, etc.

The debugging experience is just like when debugging any other local application.

Figure 5 — Debugger breaks at the hello world function

You’ve seen how to manually debug and test your lambda function, however, for real production code you’ll probably want to write automated integration tests.

Adding Integration Tests

Writing automated integration tests enables you to test your application in a consistent and reproducible way.

Your serverless application can run locally, while using other resources such as S3 buckets or DynamoDB database running on AWS. All without having to deploy the Lambda code to AWS.

This works best as part of a CI/CD pipeline, which can validate that your code works before actually publishing it to AWS.

To get started with integration tests, add pytest to your project as a dev dependency.

pipenv install --dev pytest

Now add a tests folder and an integration tests file, your project structure should look like this:

Figure 6 — Project structure with integration tests

Chalice exposes the local API gateway as a class that you can initialize in your test fixture.

Add the following code to the integration test file:

@pytest.fixture

def api():

return LocalGateway(app.app,Config())

Now let’s add an integration test that calls our Hello World function:

def test_get_hello_world(api):

response = api.handle_request(method='GET', path='/',

headers={}, body=None)

print(response)

assert response['statusCode'] == HTTPStatus.OK

response = json.loads(response['body'])

assert response['hello'] == 'world'

Running the test invokes your Lambda functions via a simulated API gateway. Using PyCharm, it’s possible to debug the execution just like we would debug any other test suite.

The example here is a simple Hello World. In the real world your function may be more complex and call other AWS services, such as DynamoDB.

When running locally, the Lambda will still call out to the external services using your local AWS credentials.

You’ve seen how to troubleshoot Lambda functions when using AWS Chalice, both in manual and automated testing.

Running and testing serverless functions locally should save you time and money since you don’t need to re-upload your functions on every change. Additionally local invocations are always free.

Hopefully it will aid you in your serverless development.

You can find the complete code example at the following repository: https://github.com/alexgelman/troubleshooting-chalice-demo