Paternoster
Paternoster enables ansible playbooks to be run like normal bash or python scripts. It parses the given parameters using python's argparse and the passes them on to the actual playbook via the ansible API. In addition it provides an automated way to run commands as another user, which can be used to give normal shell users special privileges, while still having a sleek and easy to understand user interface.
Ansible 2.1.x to 2.10.x as well as python 2.7 to 3.8 is supported and tested automatically. We recommend using ansible 2.8+ and python 3.6+.
Once everything is set up, a paternoster script can be used like this:
$ create-user --help
usage: create-user [-h] -u USERNAME [-v]
Create a user.
required arguments:
-u USERNAME, --username USERNAME
name of the user to create
optional arguments:
-h, --help show this help message and exit
-v, --verbose run with a lot of debugging output
$ create-user -u luto
creating user luto
The script looks like a normal ansible playbook, except for a few additions. Firstly, it uses a different shebang-line, which kicks off paternoster instead of ansible. Secondly, there is a special play at the beginning of the playbook, which contains the configuration for parameter parsing and other features.
#!/usr/bin/env paternoster
- hosts: paternoster
vars:
description: Create a user.
parameters:
- name: username
short: u
help: "name of the user to create"
type: paternoster.types.restricted_str
required: yes
type_params:
regex: "^[a-z]+$"
- hosts: localhost
tasks:
- debug: msg="creating user {{ param_username }}"
For more information on how to develop scripts using paternoster, please refer
the the corresponding sub-document:
doc/script_development.md
.
Privilege Escalation
Paternoster also provides an automated way to run commands as another user. To
use this feature, set the become_user
to the desired username. This causes
paternoster to execute itself as the given user using sudo. For this to work a
sudoers-config has to be created by the developer.
Please refer to the Deployment section of this document for further details.
Deployment
Python-Module
The python module can be installed using pip: pip install paternoster
.
Note that this is the only distribution packaged by us. We do not and cannot check the content of all other, following methods.
Fedora
Paternoster is also available as a Fedora package.
dnf install paternoster
RHEL/CentOS
Paternoster is also available in EPEL for RHEL7, RHEL8, CentOS7 and CentOS8.
yum install epel-release
yum install paternoster
AUR
Paternoster is also available as an AUR package for arch linux.
sudo
If you are planning to let users execute certain commands as root,
a few changes to your sudo
-configuration are needed. boils down to:
ALL ALL = NOPASSWD: /usr/local/bin/your-script-name
This line allows any user to execute the given command as root.
Please refer to the sudoers(5)
-manpage for details.
Notes
- This library makes use of the tldextract-module. Internally this
relies on a list of top level domains, which changes every so often.
Execute the
tldextract --update
-command as root in a cronjob or similar to keep the list up to date.
Library-Development
Most tasks can be achieved by writing scripts only. Therefore, the library does not need to be changed in most cases. Sometimes it might be desirable to provide a new type or feature to all other scripts. To fulfill these needs, the following section outlines the setup and development process for library.
Setup
To get a basic environment up and running, use the following commands:
virtualenv venv --python python2.7
source venv/bin/activate
python setup.py develop
pip install -r requirements.txt
pre-commit install --overwrite --install-hooks
This project uses Python 2.7
, because Python 3.x
is not yet supported by
ansible. All non-ansible code is tested with python 3 as well.
Vagrant
Most features, where unit tests suffice can be tested using a virtualenv only.
If your development relies on the sudo-mechanism, you can spin up a [vagrant
VM][] which provides a dummy uberspace-add-domain
-script as well as the
library-code in the /vagrant
-directory.
vagrant up
vagrant ssh
And inside the host:
Last login: Wed Aug 3 17:23:02 2016 from 10.0.2.2
[vagrant@localhost ~]$ uberspace-add-domain -d a.com -v
PLAY [test play] ************** (...)
If you want to add your own scripts, just add the corresponding files in
vagrant/files/scripts
. You can deploy it using the following command:
ansible-playbook vagrant/site.yml --tags scripts
. Once your script has been
deployed, you can just edit the source file to make further changes, as the file
is symlinked, not copied.
Tests
Linter
To lint the source code, you can run:
tox -e lint
Unit Tests
The core functionality of this library can be tested using the tox
- command.
If only Python 2.x
or 3.x
should be tested, the -e
parameter can be
used, like so: tox -e py36-ansible23
, tox -e py27-ansible22
. New tests
should be added to the paternoster/test
-directory. Please refer to the
pytest-documentation for further details.
NOTE: you might need to install the the proper "devel" package, for the Python versions you want to test.
Integration Tests
Some features (like the become_user
function) require a correctly setup Linux
environment. They can be tested using the provided ansible playbooks in
vagrant/tests
.
The playbooks can be invoked using the run_integration_tests.py
-utility:
$ ./vagrant/run_integration_tests.py --file test_variables.yml
=== running test_variables.yml with ansible>=2.1,<2.2
=== running test_variables.yml with ansible>=2.2,<2.3
=== running test_variables.yml with ansible>=2.3,<2.4
$ ./vagrant/run_integration_tests.py ansible22 --file test_variables.yml
=== running test_variables.yml with ansible>=2.2,<2.3
$ ./vagrant/run_integration_tests.py --help
usage: run_integration_tests.py [-h] [--file FILE]
[{ansible21,ansible22,ansible23,all}]
Run paternoster integration tests.
(...)
Boilerplate
A typical ansible playbook for a system-test might look like this:
- name: give this test a proper name
hosts: all
tasks:
- include: drop_script.yml
vars:
ignore_script_errors: yes
script_params: --some-parameter
playbook: |
- hosts: all
tasks:
- debug: msg="hello world"
script: |
#!/bin/env python2.7
# some python code to test
- assert:
that:
- "script.stdout_lines[0] == 'something'"
Most of the heavy lifting is done by the included drop_script.yml
-file. It
creates the required python-script & playbook, executes it and stores the result
in the script
-variable. After the execution, all created files are removed.
After the script has been executed, the assert
-module
can be used to check the results.
There are several parameters to control the behavior of drop_script.yml
:
Name | Optional | Description |
---|---|---|
script |
no | the content of a python script to save as /usr/local/bin/uberspace-unittest and execute |
playbook |
yes (default: empty) | the content of a playbook to save as /opt/uberspace/playbooks/uberspace-unittest.yml |
ignore_script_errors |
yes (default: false ) |
whether to continue even if python script has a non-zero exitcode |
script_params |
yes (default: empty) | command line parameters for the script (e.g. "--domain foo.com" ) |
Releasing a new version
Assuming you have been handed the required credentials, a new version can be released as follows.
- adapt the version in
setup.py
, according to semver - commit this change as
Version 1.2.3
- tag the resulting commit as
v1.2.3
- push the new tag as well as the
master
branch - update the package on PyPI:
rm dist/*
python setup.py sdist bdist_wheel
twine upload dist/*
License
All code in this repository (including this document) is licensed under the MIT license. The logo (both the png and svg versions) is licensed unter the CC-BY-NC-ND 4.0 license.