wsgi app #0

Supports: xenial


This charm should be able to serve most simple WSGI applications. It can hook into postgresql or mongodb databases (whose locations will be available to the app as DATABASE_URL and MONGO_URL environment variables), provides HTTP interfaces for load-balancing, and you can pass any extra environment variables for your application through the environment_variables config option.


This repo is a template charm for any juju deployed wsgi service. As is, this charm deploys an example wsgi service with nagios checks and simple rolling upgrades.

You can re-use this charm to deploy any wsgi service by updating the playbook.yaml file. All of the wsgi functionality is provided by a reusable wsgi-app ansible role (see roles/wsgi-app) together with the gunicorn charm.

Disclaimer: this template does not try to explain what's possible with either ansible or juju - but if you know a bit about both, it will show you how you can easily use them together.

Deploying the charm

Make sure you've got a bootstrapped juju environment ready, and then:

$ mkdir -p ~/charms/precise && cd ~/charms/precise
$ git clone
$ cd charm-bootstrap-wsgi
$ make deploy

You should now be able to curl your service to see it working:

$ make curl
juju run --service wsgi-example "curl -s http://localhost:8080"
- MachineId: "1"
  Stdout: 'It works! Revision 1'
  UnitId: charm-bootstrap-wsgi/0
- MachineId: "2"
  Stdout: 'It works! Revision 1'
  UnitId: charm-bootstrap-wsgi/1

You can also see the output of all the configured nagios checks, including the check_http added by the playbook, by running:

$ make nagios

Your custom deployment code

To deploy your own custom wsgi application, open up the playbook.yml In addition to the wsgi-app reusable role and the optional nagios reusable role (nrpe-external-master), it only has two tasks:

  • installing any package dependencies
  • Re-rendering the app's config file (and triggering a wsgi restart)

If you find yourself needing to do more than this, let me know :-)

For simplicity, the default example app is deployed from the charm itself with the archived code in the charm's files directory. But the wsgi-app role also allows you to define a code_assets_uri, which if set, will be used instead of the charm's files directory.

The nagios check used for your app can be updated by adjusting the check_params passed to the role in playbook.yml (or you can additionally add further nagios checks depending on your needs).

A rolling upgrade example

Assuming you've already deployed the example service, we first update the configuration so that the units continue to run the 'r1' build (current_symlink), while simultaneously ensuring that the 'r2' build is installed and ready to run:

$ juju set wsgi-example current_symlink=r1 build_label=r2

Next, manually set just one unit to use the r2 build:

$ juju run --unit wsgi-example/0 "CURRENT_SYMLINK=r2 actions/set-current-symlink"

PLAY [localhost] **************************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [wsgi-app | Manually set current symlink.] ******************************
changed: [localhost]

NOTIFIED: [wsgi-app | Restart wsgi] *******************************************
changed: ...

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0

Verify that the new revision is working correctly on the one instance:

$ make curl
juju run --service wsgi-example "curl -s http://localhost:8080"
- MachineId: "1"
  Stdout: 'It works! Revision 2'
  UnitId: wsgi-example/0
- MachineId: "2"
  Stdout: 'It works! Revision 1'
  UnitId: wsgi-example/1

Update any others, or when you're confident, update the full set with:

$ juju set wsgi-example current_symlink=r2

and then verify that all units are service the latest with make curl again.

Note about test Dependencies

The makefile to run tests requires the following dependencies

  • python-nose
  • python-mock
  • python-flake8

installable via:

$ sudo apt-get install python-nose python-mock python-flake8


(string) A space-separated list of apt dependencies to install
(string) The filename for the archive - e.g. "code_archive.tgz"
(string) The build label given to the code archive and corresponding to the path from which the archived code should be installed. For example, a value of r'2' tells the charm to use the archive at 'r2/code_archive.tgz'.
(string) An optional URI to download the archive from This shouldn't be the whole URI but the earlier part (e.g.: '') for this format: {{ code_assets_uri }}/{{ build_label }}/{{ archive_filename }} NB: The URI should *not* include a trailing slash
(string) The symlink of the code to run. The default of 'latest' will use the most recently added build on the instance. Specifying a differnt label (eg. "r235") will symlink to that directory assuming it has been previously added to the instance.
(string) A space separated list of environment variables for the app - in Bash variable syntax
(string) The host_name value in nagios service config files written when related to nrpe-external-master are prefixed with this value.
(string) The hostname to use in the Host: header of the check_http Nagios check when testing the health of the app server.
(int) The expected HTTP status of the nagios check
(string) A string to check for inside the index page response - for nagios to test
(string) The path within the URI to the index page of the website - for nagios to test
(string) A comma-separated list of nagios servicegroups. If left empty, the nagios_context will be used as the servicegroup.
(string) The location of the pip-cache to install requirements from. If pip-cache is present, pip will not attempt to connect to PyPi, and instead look for requirements in the specified cache folder.
(string) The location of the requirements file within the archive. Requirements in this file will be installed with `pip install`
(string) After the code is extracted, the charm will run: $ make <update_make_target> in the project directory. You can define this make target in your project to run any commands necessary to update your app
(string) The port of for any incoming webservice relations.
(string) The protocol of for any incoming webservice relations.
(string) The WSGI application within the archive, in Python notation E.g.: my_app.wsgi:application