This is the contribution guide to the infrastructure which is based on Ansible and OpenStack. If you’re a seasoned Free Software contributor looking for a quick start, take a look at the list of bugs and features, otherwise keep reading.


If you want to contribute to the Enough code base, take a look at the repository.

Bugs and features list

Each service under the domain can be worked on independently and have their own integration tests. There is no need to understand how Weblate is deployed if you’re improving Discourse, for instance.


All contributors are organized horizontally

  • People with access to an exclusive resource must register themselves in the team directory

Getting started

  • git clone

Running tests

  • Install Docker.

The tests/ script builds a docker image suitable for running the tests, with all the required dependencies, based on a Debian GNU/Linux buster. The following volumes are bind-mounted:

  • ~/.enough
  • ~/.ansible
  • the root of the infrastructure repository

The working directory, in the container, is the root of the infrastructure repository.

Running tests that do not require OpenStack

There is no need to have OpenStack credentials to run these tests.


Running tests that require OpenStack


The tests running without OpenStack only cover a fraction of what Enough does. To verify that a playbook actually works, it needs to be run on a live host and tests must check that is working. For instance the tests for weblate request that the weblate server sends a mail and verify it is relayed by the postfix server.

When modifying a role or a playbook in the directory playbooks/ABC one is expected to add a test for the new behavior and verify it runs successfully:

  • tests/ tox -e ABC

When relevant, integration tests should be created as icinga monitoring checks so they can be run on a regular basis in the production environment to verify it keeps working.

Obtain an API token

Most integration tests need a publicly available DNS server. The provides a publicly available API to delegate a domain to the designated DNS server. Members of the group enough can sign-in, others can request access.

../_images/api0.png ../_images/api1.png

The Token: value displayed after signing in must be set to the ENOUGH_API_TOKEN environment variable.

  • ENOUGH_API_TOKEN=XXXXXXX tests/ tox -e bind

Set the OpenStack credentials using

Assuming you have your own OVH tenant or one was provided to you, the credentials must be set in environment variables as follows:

  • source

Set the OpenStack credentials using clouds.yml

An alternative to is to use clouds.yml file:

  • cp clouds.provider.yml inventory/group_vars/all/clouds.yml

Note that file is ignored if inventory/group_vars/all/clouds.yml exists.


  • tests/ tox -e <service name>


If the command fails, because of a network failure or any other reason, it is safe to run it again. It is idempotent and will re-use the environment from the failed test.

The list of service names (i.e. tox test environments) is in the tox.ini file. It is possible to skip some steps to speed up test debugging:

$ tox -e bind -- --help playbooks
custom options:
  --enough-no-create    Do not run the create step
  --enough-no-tests     Do not run the tests step
  --enough-no-destroy   Do not run the destroy step
$ tests/ tox -e openvpn -- --enough-no-destroy playbooks/openvpn/tests

The domain name used for testing is in .pytest_cache/d/dotenough/bind.test/inventory/group_vars/all/domain.yml, where bind must be replaced by the name of the service. It is handy for debugging (i.e. browsing the web interface of a service, ssh to a machine that failed to run properly, etc.)

If a test fails, it will not destroy the resources provisioned for the test, they must be destroyed explicitly with something like:

  • tests/ tox -e openvpn -- --enough-no-create --enough-no-tests playbooks/openvpn/tests

Upgrade testing

To verify that a service (icinga for instance) can be upgraded from a given Enough version (2.0.7 for instance), use:

$ source
$ tests/ --upgrade 2.0.7 icinga
... performs the following steps:

  • checkout the 2.0.7 tag into ../infrastructure-versions/1.0.7/infrastructure
  • run tox -e icinga from the 2.0.7 directory and keep the hosts
  • run tox -e icinga from the current version, re-using the hosts with the icinga version installed from 2.0.7

Debugging tests

Use --log-cli-level switch in order to increase the test log level, following switches are pytest ones:

  • tests/ tox -e py3 -- --log-cli-level=DEBUG -s -x tests/enough/common/

To execute only one test:

  • tests/ tox -e py3 -- tests/enough/common/

Control-C won’t work if you’re trying to stop the tests, docker kill enough-tox should be used instead.

There should not be any leftover after a test involving OpenStack fails, because the fixtures are supposed to thoroughly cleanup. But bugs are to be expected in a test environment and it may be necessary to manually remove leftovers, using the openstack command like so:

  • tests/ env OS_CLIENT_CONFIG_FILE=~/.enough/dev/inventory/clouds.yml openstack --os-cloud production stack list
  • tests/ env OS_CLIENT_CONFIG_FILE=~/.enough/dev/clone-clouds.yml openstack --os-cloud production stack list

In case leftover are manually deleted using stack delete command, the following directory must be manually removed: .tox/<test environment>/.pytest_cache/, for example .tox/py3/.pytest_cache/.

Execute Ansible on the test infrastructure

Display content of /path/to/a/file from bind-host when icinga test environment is used:

$ tests/ .tox/icinga/bin/ansible bind-host \
   -i .tox/icinga/.pytest_cache/d/dotenough/icinga.test/inventory \
   -eansible_ssh_extra_args="'-i {{ ssh_private_keyfile }}'" \
   -mraw cat /path/to/a/file

Check the value of an ansible variable:

$ tests/ .tox/icinga/bin/ansible bind-host \
   -i .tox/icinga/.pytest_cache/d/dotenough/icinga.test/inventory \
   -eansible_ssh_extra_args="'-i {{ ssh_private_keyfile }}'" \
   -m debug -avar=ansible_host

Ansible repository layout

The ansible repository groups playbooks and roles in separate directories to reduce the number of files to consider when working on improving a playbook or a service.

  • playbooks/authorized_keys: distribute SSH public keys
  • playbooks/backup: daily VMs snapshots
  • playbooks/bind: DNS server and client
  • playbooks/letsencrypt-nginx: nginx reverse proxy with letsencrypt integration
  • playbooks/icinga: resources monitoring
  • playbooks/infrastructure: VMs creation and firewalling
  • playbooks/postfix: outgoing mail relay for all VMs
  • etc.

The other scenarii found in the playbooks directory are services such as weblate or discourse.

The toplevel directory contains the playbook that applies to the production environment. It imports playbooks found in the playbooks directory.

Managing python dependencies

  • adding a new dependency: pipenv install thepackage
  • creating the requirements*.txt files needed to create a distribution: pipenv run pipenv_to_requirements -f