mirror of
https://github.com/containers/ansible-podman-collections.git
synced 2026-04-28 11:58:48 +00:00
Podman generate systemd (#498)
* Start a new Ansible module: podman_generate_systemd
This module, when finished, will let user generate a systemd .service
unit file from a pod or a container.
If a pod is specified, then the .service file is made for the pod and
its containers.
This commit is only the beginning, with two empty functions and the
base for a Python script.
* podman_generate_systemd : Remove not needed import from __future__
* podman_generate_systemd : Import AnsibleModule and json
* podman_generate_systemd : Build podman comand from user parameters
* podman_generate_systemd : Run the podman command and manage error
* podman_generate_systemd : Manage writing of systemd files, if needed
* podman_generate_systemd : In generate_systemd(), return needed infos
In case of success in generate_systemd() function, return:
- A boolean, indicate if the state of the target systemd is modified
- A copy of the systemd unit content
* podman_generate_systemd : Write run_module() function
This function:
- Build the list of parameters user can use
- Build a default result dictionary
- Build the Ansible Module
- Generate the systemd units (with generate_systemd())
- Return the result, with module.exit_json
* podman_generate_systemd : In main(), call run_module()
* podman_generate_systemd: Return podman command, and remove debug param
The run podman command is now returned by the module, and the "debug"
option is removed.
* podman_generate_systemd: Use more direct #! to python interpreter
The run podman command is now returned by the module, and the "debug"
option is removed.
* podman_generate_systemd: Write DOCUMENTATION variable
* podman_generate_systemd: Write EXAMPLES variable
* podman_generate_systemd: Write RETURN variable
* podman_generate_systemd: Fix typo
* podman_generate_systemd: 'restart_policy, remove default
* podman_generate_systemd: Fix missing "command" return when error
When error in command execution, generate_systemd() was not returning
the command. Now it's done.
* podman_generate_systemd: Remove unnecessary return in case of fail
In generate_systemd(), in case of fail in executing the podman
command, we call module.fail_json() then we return the usual data of
generate_systemd() function.
But module.fail_json() will stop the execution of the module, so it's
not necessary to return a value from generate_systemd() after calling
module.fail_json().
* podman_generate_systemd: Remove TODO
* podman_generate_systemd: Add missing double lines between functions
* podman_generate_systemd: More readability in EXAMPLES variable
* podman_generate_systemd: EXAMPLES, add minimum podman version
When a module option require a minimum Podman version, I indicate
this required version in option description.
For example: The module option `start_timeout` use the Podman option
`--start-timout`, which was introduced in Podman 4.0.0. So, I add in
the description of `start_timeout`: Only with Podman 4.0.0 and above
Options with this information:
- start_timeout
- restart_sec
- wants
- after
- requires
* podman_generate_systemd: EXAMPLES, fix type
* podman_generate_systemd: Simpler code for wants, after and requires
`after`, `wants` and `requires` are of type list. So, no need to
test their type and manage the case where they are of type string.
* podman_generate_systemd, generate_systemd(): Improve returned command
Improve how the used Podman command, returned by generate_systemd(),
is built. Instead of print a list in a formatted string, I join the
list elements with a space char as separator.
* podman_generate_systemd: Add support for the `env` option
Add, to the podman_generate_systemd module, the support for the `env`
option. This commit include the code, the option definition for the
module, the modification of the DOCUMENTATION variable and the add of
an usage example of this new option in the EXAMPLE variable.
* podman_generate_systemd: Write the integration test
* generate_systemd: Fix of the integration test and the --env command option
On the module, I now use the command option `-e=` for the definition
of environment variables. Because the option `--env` give an error
from Podman.
Also, fix errors in the integration test for this module.
* podman_generate_systemd: Add __future__ and __metaclass__ in the module
* podman_generate_systemd: Add author github ID
* podman_generate_systemd: Use argspec type path instead of str for dest option
In the module argspec, I now use the type `path` instead of `str` for
the option `dest`. `os.path.expanduser()` is no more necessary.
* podman_generate_systemd: remove all trailing whitespaces
* podman_generate_systemd: Remove type hint for compatibility with older Python
* podman_generate_systemd: Fix Documentation
Add a default for option `new` and fix typo in `use_names` option name.
* podman_generate_systemd: restart_policy option, new choices
The choice `no` is replaced by `no-restart`
* podman_generate_systemd: dest option in documentation, set type as path
Instead of str
* DNM add CI bits for new module tests
Made by sshnaidm.
* podman_generate_systemd: Better use of fail_json()
First: When calling the method fail_json(), name the `msg` argument to
avoid confusion.
Second: In case of calling fail_json(), I now return the same
arguments than the ones returned by a success: `changed`,
`systemd_units` and `podman_command`.
For future refactoring: Instead of calling fail_json() inside
generate_systemd(), raise custom exceptions and manage calling
fail_json() or exit_json() outside of generate_systemd().
* podman_generate_systemd: Add utf-8
* podman_generate_systemd: Remove `required: false` for DOCUMENTATION var
* podman_generate_systemd: DOCUMENTATION var, use bool instead of yes/no
* podman_generate_systemd: Remove non-necessary check of restart_policy option
The possible choices for this options are set in `ansible_spec` and
will be checked by AnsibleModule code. So, no need to check manually:
`ansible_spec` won't be anything other that the possible choices.
* podman_generate_systemd: On module params, use direct access to dict
Use `module.params['']` instead of `module.params.get('')`.
Because all module's options are defined with the `argument_spec`, the
options used here exist in the params. And if the user don't set
them, the value of the option is `None`.
* podman_generate_systemd: use shlex join for command_str
* podman_generate_systemd: use same quotes for messages
* podman_generate_systemd: Replace f-strings with .format(), for Python 2
* podman_generate_systemd: Revert usage of shlex
It add weird quotes around command options like `-e='ENV_VAR=value'`.
* podman_generate_systemd: Podman command build, more Python 2 compatible
Don't use any more the asterisk to add command options to the command.
* podman_generate_systemd: Better check-mode management
Now, when check mode is enabled, the module report "changed" is
something would be changed, but w/o actually doing anything.
* podman_generate_systemd: remove all trailing whitespaces
Co-authored-by: Sébastien Gendre <seb@k-7.ch>
This commit is contained in:
parent
c9008c75b4
commit
6cf118e7a9
4 changed files with 773 additions and 0 deletions
99
.github/workflows/podman_generate_systemd.yml
vendored
Normal file
99
.github/workflows/podman_generate_systemd.yml
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
name: Podman generate systemd
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/podman_generate_systemd.yml'
|
||||
- 'ci/*.yml'
|
||||
- 'ci/run_containers_tests.sh'
|
||||
- 'ci/playbooks/containers/podman_generate_systemd.yml'
|
||||
- 'plugins/modules/podman_generate_systemd.py'
|
||||
- 'tests/integration/targets/podman_generate_systemd/**'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/podman_generate_systemd.yml'
|
||||
- 'ci/*.yml'
|
||||
- 'ci/run_containers_tests.sh'
|
||||
- 'ci/playbooks/containers/podman_generate_systemd.yml'
|
||||
- 'plugins/modules/podman_generate_systemd.py'
|
||||
- 'tests/integration/targets/podman_generate_systemd/**'
|
||||
schedule:
|
||||
- cron: 4 0 * * * # Run daily at 0:03 UTC
|
||||
|
||||
jobs:
|
||||
|
||||
test_podman_generate_systemd:
|
||||
name: Podman generate systemd ${{ matrix.ansible-version }}-${{ matrix.os || 'ubuntu-latest' }}
|
||||
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ansible-version:
|
||||
- ansible<2.10
|
||||
# - git+https://github.com/ansible/ansible.git@stable-2.11
|
||||
- git+https://github.com/ansible/ansible.git@devel
|
||||
os:
|
||||
- ubuntu-22.04
|
||||
python-version:
|
||||
- 3.9
|
||||
|
||||
steps:
|
||||
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Upgrade pip and display Python and PIP versions
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python*-wheel python*-yaml
|
||||
python -m pip install --upgrade pip
|
||||
python -V
|
||||
pip --version
|
||||
- name: Set up pip cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ github.ref }}-units-VMs
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
- name: Install Ansible ${{ matrix.ansible-version }}
|
||||
run: python3 -m pip install --user --force-reinstall --upgrade '${{ matrix.ansible-version }}'
|
||||
|
||||
- name: Build and install the collection tarball
|
||||
run: |
|
||||
rm -rf /tmp/just_new_collection
|
||||
~/.local/bin/ansible-galaxy collection build --output-path /tmp/just_new_collection --force
|
||||
~/.local/bin/ansible-galaxy collection install -vvv --force /tmp/just_new_collection/*.tar.gz
|
||||
- name: Run collection tests for Podman generate systemd
|
||||
run: |
|
||||
export PATH=~/.local/bin:$PATH
|
||||
echo "Run ansible version"
|
||||
command -v ansible
|
||||
ansible --version
|
||||
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-dev.cfg
|
||||
if [[ '${{ matrix.ansible-version }}' == 'ansible<2.10' ]]; then
|
||||
export ANSIBLE_CONFIG=$(pwd)/ci/ansible-2.9.cfg
|
||||
fi
|
||||
echo $ANSIBLE_CONFIG
|
||||
command -v ansible-playbook
|
||||
pip --version
|
||||
python --version
|
||||
ansible-playbook --version
|
||||
ansible-playbook -vv ci/playbooks/pre.yml \
|
||||
-e host=localhost \
|
||||
-i localhost, \
|
||||
-e ansible_connection=local \
|
||||
-e setup_python=false
|
||||
TEST2RUN=podman_generate_systemd ./ci/run_containers_tests.sh
|
||||
shell: bash
|
||||
8
ci/playbooks/containers/podman_generate_systemd.yml
Normal file
8
ci/playbooks/containers/podman_generate_systemd.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
- hosts: all
|
||||
gather_facts: true
|
||||
tasks:
|
||||
- include_role:
|
||||
name: podman_generate_systemd
|
||||
vars:
|
||||
ansible_python_interpreter: "{{ _ansible_python_interpreter }}"
|
||||
572
plugins/modules/podman_generate_systemd.py
Normal file
572
plugins/modules/podman_generate_systemd.py
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# 2022, Sébastien Gendre <seb@k-7.ch>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: podman_generate_systemd
|
||||
author:
|
||||
- Sébastien Gendre (@CyberFox001)
|
||||
short_description: Generate systemd unit from a pod or a container
|
||||
description:
|
||||
- Generate systemd .service unit file(s) from a pod or a container
|
||||
- Support Ansible check mode
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the pod or container to export
|
||||
type: str
|
||||
required: true
|
||||
dest:
|
||||
description:
|
||||
- Destination of the generated systemd unit file(s)
|
||||
type: path
|
||||
new:
|
||||
description:
|
||||
- Generate unit files that create containers and pods, not only start them.
|
||||
- Refer to podman-generate-systemd(1) man page for more information.
|
||||
type: bool
|
||||
default: false
|
||||
restart_policy:
|
||||
description:
|
||||
- Restart policy of the service
|
||||
type: str
|
||||
choices:
|
||||
- no-restart
|
||||
- on-success
|
||||
- on-failure
|
||||
- on-abnormal
|
||||
- on-watchdog
|
||||
- on-abort
|
||||
- always
|
||||
restart_sec:
|
||||
description:
|
||||
- Configures the time to sleep before restarting a service (as configured with restart-policy).
|
||||
- Takes a value in seconds.
|
||||
- Only with Podman 4.0.0 and above
|
||||
type: int
|
||||
start_timeout:
|
||||
description:
|
||||
- Override the default start timeout for the container with the given value in seconds.
|
||||
- Only with Podman 4.0.0 and above
|
||||
type: int
|
||||
stop_timeout:
|
||||
description:
|
||||
- Override the default stop timeout for the container with the given value in seconds.
|
||||
type: int
|
||||
env:
|
||||
description:
|
||||
- Set environment variables to the systemd unit files.
|
||||
- Keys are the environment variable names, and values are the environment variable values
|
||||
- Only with Podman 4.3.0 and above
|
||||
type: dict
|
||||
use_names:
|
||||
description:
|
||||
- Use name of the containers for the start, stop, and description in the unit file.
|
||||
type: bool
|
||||
default: true
|
||||
container_prefix:
|
||||
description:
|
||||
- Set the systemd unit name prefix for containers.
|
||||
- If not set, use the default defined by podman, C(container).
|
||||
- Refer to podman-generate-systemd(1) man page for more information.
|
||||
type: str
|
||||
pod_prefix:
|
||||
description:
|
||||
- Set the systemd unit name prefix for pods.
|
||||
- If not set, use the default defined by podman, C(pod).
|
||||
- Refer to podman-generate-systemd(1) man page for more information.
|
||||
type: str
|
||||
separator:
|
||||
description:
|
||||
- Systemd unit name separator between the name/id of a container/pod and the prefix.
|
||||
- If not set, use the default defined by podman, C(-).
|
||||
- Refer to podman-generate-systemd(1) man page for more information.
|
||||
type: str
|
||||
no_header:
|
||||
description:
|
||||
- Do not generate the header including meta data such as the Podman version and the timestamp.
|
||||
type: bool
|
||||
default: false
|
||||
after:
|
||||
description:
|
||||
- Add the systemd unit after (C(After=)) option, that ordering dependencies between the list of dependencies and this service.
|
||||
- This option may be specified more than once.
|
||||
- User-defined dependencies will be appended to the generated unit file
|
||||
- But any existing options such as needed or defined by default (e.g. C(online.target)) will not be removed or overridden.
|
||||
- Only with Podman 4.0.0 and above
|
||||
type: list
|
||||
elements: str
|
||||
wants:
|
||||
description:
|
||||
- Add the systemd unit wants (C(Wants=)) option, that this service is (weak) dependent on.
|
||||
- This option may be specified more than once.
|
||||
- This option does not influence the order in which services are started or stopped.
|
||||
- User-defined dependencies will be appended to the generated unit file
|
||||
- But any existing options such as needed or defined by default (e.g. C(online.target)) will not be removed or overridden.
|
||||
- Only with Podman 4.0.0 and above
|
||||
type: list
|
||||
elements: str
|
||||
requires:
|
||||
description:
|
||||
- Set the systemd unit requires (Requires=) option.
|
||||
- Similar to wants, but declares a stronger requirement dependency.
|
||||
- Only with Podman 4.0.0 and above
|
||||
type: list
|
||||
elements: str
|
||||
executable:
|
||||
description:
|
||||
- C(Podman) executable name or full path
|
||||
type: str
|
||||
default: podman
|
||||
requirements:
|
||||
- Podman installed on target host
|
||||
notes:
|
||||
- You can store your systemd unit files in C(/etc/systemd/user/) for system wide usage
|
||||
- Or you can store them in C(~/.config/systemd/user/) for usage at a specific user
|
||||
- If you indicate a pod, the systemd units for it and all its containers will be generated
|
||||
- Create all your pods, containers and their dependencies before generating the systemd files
|
||||
- If a container or pod is already started before you do a C(systemctl daemon reload),
|
||||
systemd will not see the container or pod as started
|
||||
- Stop your container or pod before you do a C(systemctl daemon reload),
|
||||
then you can start them with C(systemctl start my_container.service)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Exemple of creating a container and integrate it into systemd
|
||||
- name: A postgres container must exist, stopped
|
||||
containers.podman.podman_container:
|
||||
name: postgres_local
|
||||
image: docker.io/library/postgres:latest
|
||||
state: stopped
|
||||
|
||||
- name: Systemd unit files for postgres container must exist
|
||||
containers.podman.podman_generate_systemd:
|
||||
name: postgres_local
|
||||
path: ~/.config/systemd/user/
|
||||
|
||||
- name: Postgres container must be started and enabled on systemd
|
||||
ansible.builtin.systemd:
|
||||
name: container-postgres_local
|
||||
daemon_reload: yes
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
|
||||
# Generate the unit files, but store them on an Ansible variable
|
||||
# instead of writting them on target host
|
||||
- name: Systemd unit files for postgres container must be generated
|
||||
containers.podman.podman_generate_systemd:
|
||||
name: postgres_local
|
||||
register: postgres_local_systemd_unit
|
||||
|
||||
# Generate the unit files with environment variables sets
|
||||
- name: Systemd unit files for postgres container must be generated
|
||||
containers.podman.podman_generate_systemd:
|
||||
name: postgres_local
|
||||
env:
|
||||
POSTGRES_USER: my_app
|
||||
POSTGRES_PASSWORD: example
|
||||
register: postgres_local_systemd_unit
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
systemd_units:
|
||||
description: A copy of the generated systemd .service unit(s)
|
||||
returned: always
|
||||
type: dict
|
||||
sample: {
|
||||
"container-postgres_local": " #Content of the systemd .servec unit for postgres_local container",
|
||||
"pod-my_webapp": " #Content of the systemd .servec unit for my_webapp pod",
|
||||
}
|
||||
podman_command:
|
||||
description: A copy of the podman command used to generate the systemd unit(s)
|
||||
returned: always
|
||||
type: str
|
||||
sample: "podman generate systemd my_webapp"
|
||||
'''
|
||||
|
||||
|
||||
import os
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import json
|
||||
|
||||
|
||||
RESTART_POLICY_CHOICES = [
|
||||
'no-restart',
|
||||
'on-success',
|
||||
'on-failure',
|
||||
'on-abnormal',
|
||||
'on-watchdog',
|
||||
'on-abort',
|
||||
'always',
|
||||
]
|
||||
|
||||
|
||||
def generate_systemd(module):
|
||||
'''Generate systemd .service unit file from a pod or container.
|
||||
|
||||
Parameter:
|
||||
- module (AnsibleModule): An AnsibleModule object
|
||||
|
||||
Returns (tuple[bool, list[str], str]):
|
||||
- A boolean which indicate whether the targeted systemd state is modified
|
||||
- A copy of the generated systemd .service units content
|
||||
- A copy of the command, as a string
|
||||
'''
|
||||
# Flag which indicate whether the targeted system state is modified
|
||||
changed = False
|
||||
|
||||
# Build the podman command, based on the module parameters
|
||||
command_options = []
|
||||
|
||||
# New option
|
||||
if module.params['new']:
|
||||
command_options.append('--new')
|
||||
|
||||
# Restart policy option
|
||||
restart_policy = module.params['restart_policy']
|
||||
if restart_policy:
|
||||
# add the restart policy to options
|
||||
if restart_policy == 'no-restart':
|
||||
restart_policy = 'no'
|
||||
command_options.append(
|
||||
'--restart-policy={restart_policy}'.format(
|
||||
restart_policy=restart_policy,
|
||||
),
|
||||
)
|
||||
|
||||
# Restart-sec option (only for Podman 4.0.0 and above)
|
||||
restart_sec = module.params['restart_sec']
|
||||
if restart_sec:
|
||||
command_options.append(
|
||||
'--restart-sec={restart_sec}'.format(
|
||||
restart_sec=restart_sec,
|
||||
),
|
||||
)
|
||||
|
||||
# Start-timeout option (only for Podman 4.0.0 and above)
|
||||
start_timeout = module.params['start_timeout']
|
||||
if start_timeout:
|
||||
command_options.append(
|
||||
'--start-timeout={start_timeout}'.format(
|
||||
start_timeout=start_timeout,
|
||||
),
|
||||
)
|
||||
|
||||
# Stop-timeout option
|
||||
stop_timeout = module.params['stop_timeout']
|
||||
if stop_timeout:
|
||||
command_options.append(
|
||||
'--stop-timeout={stop_timeout}'.format(
|
||||
stop_timeout=stop_timeout,
|
||||
),
|
||||
)
|
||||
|
||||
# Use container name(s) option
|
||||
if module.params['use_names']:
|
||||
command_options.append('--name')
|
||||
|
||||
# Container-prefix option
|
||||
container_prefix = module.params['container_prefix']
|
||||
if container_prefix:
|
||||
command_options.append(
|
||||
'--container-prefix={container_prefix}'.format(
|
||||
container_prefix=container_prefix,
|
||||
),
|
||||
)
|
||||
|
||||
# Pod-prefix option
|
||||
pod_prefix = module.params['pod_prefix']
|
||||
if pod_prefix:
|
||||
command_options.append(
|
||||
'--pod-prefix={pod_prefix}'.format(
|
||||
pod_prefix=pod_prefix,
|
||||
),
|
||||
)
|
||||
|
||||
# Separator option
|
||||
separator = module.params['separator']
|
||||
if separator:
|
||||
command_options.append(
|
||||
'--separator={separator}'.format(
|
||||
separator=separator,
|
||||
),
|
||||
)
|
||||
|
||||
# No-header option
|
||||
if module.params['no_header']:
|
||||
command_options.append('--no-header')
|
||||
|
||||
# After option (only for Podman 4.0.0 and above)
|
||||
after = module.params['after']
|
||||
if after:
|
||||
for item in after:
|
||||
command_options.append(
|
||||
'--after={item}'.format(
|
||||
item=item,
|
||||
),
|
||||
)
|
||||
|
||||
# Wants option (only for Podman 4.0.0 and above)
|
||||
wants = module.params['wants']
|
||||
if wants:
|
||||
for item in wants:
|
||||
command_options.append(
|
||||
'--wants={item}'.format(
|
||||
item=item,
|
||||
)
|
||||
)
|
||||
|
||||
# Requires option (only for Podman 4.0.0 and above)
|
||||
requires = module.params['requires']
|
||||
if requires:
|
||||
for item in requires:
|
||||
command_options.append(
|
||||
'--requires={item}'.format(
|
||||
item=item,
|
||||
),
|
||||
)
|
||||
|
||||
# Environment variables (only for Podman 4.3.0 and above)
|
||||
environment_variables = module.params['env']
|
||||
if environment_variables:
|
||||
for env_var_name, env_var_value in environment_variables.items():
|
||||
command_options.append(
|
||||
"-e='{env_var_name}={env_var_value}'".format(
|
||||
env_var_name=env_var_name,
|
||||
env_var_value=env_var_value,
|
||||
),
|
||||
)
|
||||
|
||||
# Set output format, of podman command, to json
|
||||
command_options.extend(['--format', 'json'])
|
||||
|
||||
# Full command build, with option included
|
||||
# Base of the command
|
||||
command = [
|
||||
module.params['executable'], 'generate', 'systemd',
|
||||
]
|
||||
# Add the options to the commande
|
||||
command.extend(command_options)
|
||||
# Add pod or container name to the command
|
||||
command.append(module.params['name'])
|
||||
# Build the string version of the command, only for module return
|
||||
command_str = ' '.join(command)
|
||||
|
||||
# Run the podman command to generated systemd .service unit(s) content
|
||||
return_code, stdout, stderr = module.run_command(command)
|
||||
|
||||
# In case of error in running the command
|
||||
if return_code != 0:
|
||||
# Print informations about the error and return and empty dictionary
|
||||
message = 'Error generating systemd .service unit(s).'
|
||||
message += ' Command executed: {command_str}'
|
||||
message += ' Command returned with code: {return_code}.'
|
||||
message += ' Error message: {stderr}.'
|
||||
module.fail_json(
|
||||
msg=message.format(
|
||||
command_str=command_str,
|
||||
return_code=return_code,
|
||||
stderr=stderr,
|
||||
),
|
||||
changed=changed,
|
||||
systemd_units={},
|
||||
podman_command=command_str,
|
||||
)
|
||||
|
||||
# In case of command execution success, its stdout is a json
|
||||
# dictionary. This dictionary is all the generated systemd units.
|
||||
# Each key value pair is one systemd unit. The key is the unit name
|
||||
# and the value is the unit content.
|
||||
|
||||
# Load the returned json dictionary as a python dictionary
|
||||
systemd_units = json.loads(stdout)
|
||||
|
||||
# Write the systemd .service unit(s) content to file(s), if
|
||||
# requested
|
||||
if module.params['dest']:
|
||||
try:
|
||||
systemd_units_dest = module.params['dest']
|
||||
# If destination don't exist
|
||||
if not os.path.exists(systemd_units_dest):
|
||||
# If not in check mode, make it
|
||||
if not module.check_mode:
|
||||
os.makedirs(systemd_units_dest)
|
||||
changed = True
|
||||
# If destination exist but not a directory
|
||||
if not os.path.isdir(systemd_units_dest):
|
||||
# Stop and tell user that the destination is not a directry
|
||||
message = "Destination {systemd_units_dest} is not a directory."
|
||||
message += " Can't save systemd unit files in."
|
||||
module.fail_json(
|
||||
msg=message.format(
|
||||
systemd_units_dest=systemd_units_dest,
|
||||
),
|
||||
changed=changed,
|
||||
systemd_units=systemd_units,
|
||||
podman_command=command_str,
|
||||
)
|
||||
|
||||
# Write each systemd unit, if needed
|
||||
for unit_name, unit_content in systemd_units.items():
|
||||
# Build full path to unit file
|
||||
unit_file_name = unit_name + '.service'
|
||||
unit_file_full_path = os.path.join(
|
||||
systemd_units_dest,
|
||||
unit_file_name,
|
||||
)
|
||||
|
||||
# See if we need to write the unit file, default yes
|
||||
need_to_write_file = True
|
||||
# If the unit file already exist, compare it with the
|
||||
# generated content
|
||||
if os.path.exists(unit_file_full_path):
|
||||
# Read the file
|
||||
with open(unit_file_full_path, 'r') as unit_file:
|
||||
current_unit_file_content = unit_file.read()
|
||||
# If current unit file content is the same as the
|
||||
# generated content
|
||||
if current_unit_file_content == unit_content:
|
||||
# We don't need to write it
|
||||
need_to_write_file = False
|
||||
|
||||
# Write the file, if needed
|
||||
if need_to_write_file:
|
||||
with open(unit_file_full_path, 'w') as unit_file:
|
||||
# If not in check mode, write the file
|
||||
if not module.check_mode:
|
||||
unit_file.write(unit_content)
|
||||
changed = True
|
||||
|
||||
except Exception as exception:
|
||||
# When exception occurs while trying to write units file
|
||||
message = 'PODMAN-GENERATE-SYSTEMD-DEBUG: '
|
||||
message += 'Error writing systemd units files: '
|
||||
message += '{exception}'
|
||||
module.log(
|
||||
message.format(
|
||||
exception=exception
|
||||
),
|
||||
)
|
||||
# Return the systemd .service unit(s) content
|
||||
return changed, systemd_units, command_str
|
||||
|
||||
|
||||
def run_module():
|
||||
'''Run the module on the target'''
|
||||
# Build the list of parameters user can use
|
||||
module_parameters = {
|
||||
'name': {
|
||||
'type': 'str',
|
||||
'required': True,
|
||||
},
|
||||
'dest': {
|
||||
'type': 'path',
|
||||
'required': False,
|
||||
},
|
||||
'new': {
|
||||
'type': 'bool',
|
||||
'required': False,
|
||||
'default': False,
|
||||
},
|
||||
'restart_policy': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
'choices': RESTART_POLICY_CHOICES,
|
||||
},
|
||||
'restart_sec': {
|
||||
'type': 'int',
|
||||
'required': False,
|
||||
},
|
||||
'start_timeout': {
|
||||
'type': 'int',
|
||||
'required': False,
|
||||
},
|
||||
'stop_timeout': {
|
||||
'type': 'int',
|
||||
'required': False,
|
||||
},
|
||||
'env': {
|
||||
'type': 'dict',
|
||||
'required': False,
|
||||
},
|
||||
'use_names': {
|
||||
'type': 'bool',
|
||||
'required': False,
|
||||
'default': True,
|
||||
},
|
||||
'container_prefix': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
},
|
||||
'pod_prefix': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
},
|
||||
'separator': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
},
|
||||
'no_header': {
|
||||
'type': 'bool',
|
||||
'required': False,
|
||||
'default': False,
|
||||
},
|
||||
'after': {
|
||||
'type': 'list',
|
||||
'elements': 'str',
|
||||
'required': False,
|
||||
},
|
||||
'wants': {
|
||||
'type': 'list',
|
||||
'elements': 'str',
|
||||
'required': False,
|
||||
},
|
||||
'requires': {
|
||||
'type': 'list',
|
||||
'elements': 'str',
|
||||
'required': False,
|
||||
},
|
||||
'executable': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
'default': 'podman',
|
||||
},
|
||||
}
|
||||
|
||||
# Build result dictionary
|
||||
result = {
|
||||
'changed': False,
|
||||
'systemd_units': {},
|
||||
'podman_command': '',
|
||||
}
|
||||
|
||||
# Build the Ansible Module
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_parameters,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
# Generate the systemd units
|
||||
state_changed, systemd_units, podman_command = generate_systemd(module)
|
||||
|
||||
result['changed'] = state_changed
|
||||
result['systemd_units'] = systemd_units
|
||||
result['podman_command'] = podman_command
|
||||
|
||||
# Return the result
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
'''Main function of this script.'''
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
- name: A postgres container must exist, stopped
|
||||
containers.podman.podman_container:
|
||||
name: postgres_local
|
||||
image: docker.io/library/postgres:latest
|
||||
state: stopped
|
||||
|
||||
- name: Generate the systemd units as Ansible variables
|
||||
containers.podman.podman_generate_systemd:
|
||||
name: postgres_local
|
||||
register: postgres_local_systemd_unit
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check systemd unit are generated
|
||||
assert:
|
||||
that:
|
||||
- postgres_local_systemd_unit is succeeded
|
||||
|
||||
- name: Check systemd unit exist in registered vars
|
||||
assert:
|
||||
that:
|
||||
- item.key == "container-postgres_local"
|
||||
- item.value != None
|
||||
loop: "{{ postgres_local_systemd_unit.systemd_units | dict2items }}"
|
||||
|
||||
- name: Check podman command used to generate systemd units is returned
|
||||
assert:
|
||||
that:
|
||||
- postgres_local_systemd_unit.podman_command != ""
|
||||
|
||||
- name: Regenerate the systemd units and write them
|
||||
containers.podman.podman_generate_systemd:
|
||||
name: postgres_local
|
||||
dest: /tmp/podman_generate_systemd
|
||||
register: postgres_local_systemd_unit
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check the unit files exists
|
||||
ansible.builtin.stat:
|
||||
path: "/tmp/podman_generate_systemd/{{ item.key }}.service"
|
||||
loop: "{{ postgres_local_systemd_unit.systemd_units | dict2items }}"
|
||||
|
||||
- name: Regenerate the systemd units with all the options
|
||||
containers.podman.podman_generate_systemd:
|
||||
name: postgres_local
|
||||
new: yes
|
||||
restart_policy: on-abnormal
|
||||
restart_sec: 19
|
||||
start_timeout: 21
|
||||
stop_timeout: 23
|
||||
env:
|
||||
POSTGRES_USER: my_app
|
||||
POSTGRES_PASSWORD: example
|
||||
use_names: yes
|
||||
container_prefix: more
|
||||
pod_prefix: less
|
||||
separator: +
|
||||
no_header: yes
|
||||
after: drink.service
|
||||
wants: water.service
|
||||
requires: ice.service
|
||||
executable: /usr/bin/podman
|
||||
register: postgres_local_systemd_unit
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check the correct podman command is build
|
||||
assert:
|
||||
that:
|
||||
- postgres_local_systemd_unit.podman_command is search("postgres_local")
|
||||
- postgres_local_systemd_unit.podman_command is search("--new")
|
||||
- postgres_local_systemd_unit.podman_command is search("--restart-policy=on-abnormal")
|
||||
- postgres_local_systemd_unit.podman_command is search("--restart-sec=19")
|
||||
- postgres_local_systemd_unit.podman_command is search("--start-timeout=21")
|
||||
- postgres_local_systemd_unit.podman_command is search("--stop-timeout=23")
|
||||
- postgres_local_systemd_unit.podman_command is search("-e='POSTGRES_USER=my_app'")
|
||||
- postgres_local_systemd_unit.podman_command is search("-e='POSTGRES_PASSWORD=example'")
|
||||
- postgres_local_systemd_unit.podman_command is search("--name")
|
||||
- postgres_local_systemd_unit.podman_command is search("--container-prefix=more")
|
||||
- postgres_local_systemd_unit.podman_command is search("--pod-prefix=less")
|
||||
- postgres_local_systemd_unit.podman_command is search("--separator=+")
|
||||
- postgres_local_systemd_unit.podman_command is search("--no-header")
|
||||
- postgres_local_systemd_unit.podman_command is search("--after=drink.service")
|
||||
- postgres_local_systemd_unit.podman_command is search("--wants=water.service")
|
||||
- postgres_local_systemd_unit.podman_command is search("--requires=ice.service")
|
||||
- postgres_local_systemd_unit.podman_command is search("/usr/bin/podman")
|
||||
|
||||
- name: Remove container
|
||||
containers.podman.podman_container:
|
||||
executable: "{{ test_executable | default('podman') }}"
|
||||
name: postgres_local
|
||||
state: absent
|
||||
- name: Remove the systemd unit files directory
|
||||
ansible.builtin.file:
|
||||
path: /tmp/podman_generate_systemd
|
||||
state: absent
|
||||
Loading…
Add table
Add a link
Reference in a new issue