When we release new code, one of the main objectives is to define its the applicability within our codebase. In the context of Ansible we would certainly like to obtain the following on the new written code:
- Increased ease and speed in testing.
- Increase quality.
- Readily detect errors.
(For the moment we will leave out other aspects, however important, but not related to the subject covered in this article.)
The simplest first step is to integrate one or more tools that verify, before applying Ansible code, that the syntax is correct.
There are two syntaxes:
- the syntax of YAML code.
- the syntax of Ansible code through its keywords and modules.
As with all languages, even in Ansible linting is possible. There are many tools available that help us analyze Ansible code, and the most used ones are:
- Yamllint for YAML code
- Ansible-lint for Ansible code
Static analysis helps detect any syntax errors before executing code. With it, we can make sure that there are no syntactic errors, which means that if an issue is found, we can fix it immediately therefore speeding up the development.
Another advantage is the increase of code quality. A linter, in fact, also has the function of advising the developer on possibile best practices and remove, if present, deprecated code.
Let’s see some examples.
Ansible-lint
First of all, if it is not available, you need to install it within your environment:
1 |
pip3 install ansible-lint |
Ansible-lint can run either on an Ansible playbook list or on a list of roles directories.
For example let’s take a role to configure the nginx web server. We will have such a structure:
1 2 3 4 5 6 7 |
ansible/roles/nginx/ |-- tasks | |-- nginx_dev.yml | `-- nginx_prod.yml `-- templates |-- nuvola-dev.conf.j2 `-- nuvola-prod.conf.j2 |
So let’s try our lint on the nginx role:
1 2 3 4 |
ansible-lint nginx/ [208] File permissions not mentioned nginx/tasks/nginx_dev.yml:4 Task/Handler: NGINX DEV | NGINX configuration |
The first message tells us that we did not specify the permissions on the destination file (mode). This is not a real error but rather a good practice. This is the offending tasks:
1 2 3 4 5 6 7 |
- name: NGINX DEV | Add default nuvola website template: src: roles/nginx/templates/nuvola-dev.conf.j2 dest: /etc/nginx/nginx.conf become: true become_user: root tags: nginx_dev |
In this other example we are told that we have defined a variable without the initial space:
1 2 3 |
[206] Variables should have spaces before and after: {{ var_name }} nginx/tasks/nginx_prod.yml:10 dest: "{{path_file }}/nginx.conf" |
We’d probably be happy if we were notified at every git commit if we made a mistake or there’s a problem about the code. You can register ansible-lint as a git pre-commit hook, this way at each commit we can first run a static analysis on the content of the code and be informed immediately of any errors or malfunctions.
Yamllint
Another interesting tool for static code analysis is Yamllint, a linter for YAML files, which in addition to the validity of the syntax, reports style issues such as line length, end spaces, indentation, and more. To install it, follow the instructions on its documentation.
The easiest way to run it is directly to the YAML file:
1 2 3 4 |
yamllint ansible/roles/nginx/tasks/nginx_prod.yml ansible/roles/nginx/tasks/nginx_prod.yml 1:1 warning missing document start "---" (document-start) 12:3 error syntax error: could not find expected ':' (syntax) |
The first warning tells us that we did not enter the opening tags of a YAML document, but this setting is optional, so it is reported as a warning.
The second line indicates that there is a syntax error:
1 2 3 4 |
- name: NGINX BACKEND | NGINX configuration template: src: roles/nginx/templates/nginx.conf.j2 dest=/etc/nginx/nginx.conf |
This is promptly reported to us, without having to wait for our playbook to be interrupted during its execution.
Yamllint has a good amount of configuration options that can be set up, for example by specifying its own configuration file and default Rules. I refer you to the documentation for more details on how to customize it.
Finally, just like with Ansible-lint, Yamllint can be set up as git pre-commit hook.
We try to use good development practices in all our environments, because we are aware that the initial effort of setting up and configuring is small when compared to the benefits at every stage of software release.
Good testing!