wsgi app #16

  • By nottrobin
  • Latest version (#16)
  • trusty
  • Stable
  • Edge


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) The location of the requirements file within the archive. Requirements in this file will be installed with `pip install`
(string) A string to check for inside the index page response - for nagios to test
(int) The expected HTTP status of the nagios check
(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) The filename for the archive - e.g. "code_archive.tgz"
(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.
(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 path within the URI to the index page of the website - for nagios to test
(string) The port of for any incoming webservice relations.
(string) The WSGI application within the archive, in Python notation E.g.: my_app.wsgi:application
(string) A space-separated list of apt dependencies to install
(string) A space separated list of environment variables for the app - in Bash variable syntax
(string) The protocol of for any incoming webservice relations.
(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) 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 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.