Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add systemd generation for pods #299

Merged
merged 1 commit into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions plugins/module_utils/podman/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type

import json
import os
import shutil

Expand All @@ -21,6 +22,63 @@ def run_podman_command(module, executable='podman', args=None, expected_rc=0, ig
return rc, out, err


def generate_systemd(module, module_params, name):
"""Generate systemd unit file."""
command = [module_params['executable'], 'generate', 'systemd',
name, '--format', 'json']
sysconf = module_params['generate_systemd']
empty = {}
if sysconf.get('restart_policy'):
if sysconf.get('restart_policy') not in [
"no", "on-success", "on-failure", "on-abnormal", "on-watchdog",
"on-abort", "always"]:
module.fail_json(
'Restart policy for systemd unit file is "%s" and must be one of: '
'"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always"' %
sysconf.get('restart_policy'))
command.extend([
'--restart-policy',
sysconf['restart_policy']])
if sysconf.get('time'):
command.extend(['--time', str(sysconf['time'])])
if sysconf.get('no_header'):
command.extend(['--no-header'])
if sysconf.get('names', True):
command.extend(['--name'])
if sysconf.get('container_prefix'):
command.extend(['--container-prefix=%s' % sysconf['container_prefix']])
if sysconf.get('pod_prefix'):
command.extend(['--pod-prefix=%s' % sysconf['pod_prefix']])
if sysconf.get('separator'):
command.extend(['--separator=%s' % sysconf['separator']])
if module.params['debug'] or module_params['debug']:
module.log("PODMAN-CONTAINER-DEBUG: systemd command: %s" %
" ".join(command))
rc, systemd, err = module.run_command(command)
if rc != 0:
module.log(
"PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err)
return empty
else:
try:
data = json.loads(systemd)
if sysconf.get('path'):
if not os.path.exists(sysconf['path']):
os.makedirs(sysconf['path'])
if not os.path.isdir(sysconf['path']):
module.fail_json("Path %s is not a directory! "
"Can not save systemd unit files there!"
% sysconf['path'])
for k, v in data.items():
with open(os.path.join(sysconf['path'], k), 'w') as f:
f.write(v)
return data
except Exception as e:
module.log(
"PODMAN-CONTAINER-DEBUG: Error writing systemd: %s" % e)
return empty


def lower_keys(x):
if isinstance(x, list):
return [lower_keys(v) for v in x]
Expand Down
53 changes: 2 additions & 51 deletions plugins/module_utils/podman/podman_container_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from ansible.module_utils._text import to_bytes, to_native # noqa: F402
from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys
from ansible_collections.containers.podman.plugins.module_utils.podman.common import generate_systemd

__metaclass__ = type

Expand Down Expand Up @@ -1511,56 +1512,6 @@ def __init__(self, module, params):
self.container = PodmanContainer(
self.module, self.name, self.module_params)

def generate_systemd(self):
"""Generate systemd unit file."""
command = [self.module_params['executable'], 'generate', 'systemd',
self.name, '--format', 'json']
sysconf = self.module_params['generate_systemd']
empty = {'name': '', 'text': ''}
if sysconf.get('restart_policy'):
if sysconf.get('restart_policy') not in [
"no", "on-success", "on-failure", "on-abnormal", "on-watchdog",
"on-abort", "always"]:
self.module.fail_json(
'Restart policy for systemd unit file is "%s" and must be one of: '
'"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always"' %
sysconf.get('restart_policy'))
command.extend([
'--restart-policy',
sysconf['restart_policy']])
if sysconf.get('time'):
command.extend(['--time', str(sysconf['time'])])
if sysconf.get('no_header'):
command.extend(['--no-header'])
if sysconf.get('names', True):
command.extend(['--name'])
if sysconf.get('container_prefix'):
command.extend(['--container-prefix=%s' % sysconf['container_prefix']])
if sysconf.get('pod_prefix'):
command.extend(['--pod-prefix=%s' % sysconf['pod_prefix']])
if sysconf.get('separator'):
command.extend(['--separator=%s' % sysconf['separator']])
if self.module.params['debug'] or self.module_params['debug']:
self.module.log("PODMAN-CONTAINER-DEBUG: systemd command: %s" % " ".join(command))
rc, systemd, err = self.module.run_command(command)
if rc != 0:
self.module.log(
"PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err)
return empty
else:
try:
systemd_text = list(json.loads(systemd).values())[0]
systemd_name = list(json.loads(systemd).keys())[0]
if sysconf.get('file'):
with open(sysconf['file'], 'w') as f:
f.write(systemd_text)
return {'name': systemd_name,
'text': systemd_text}
except Exception as e:
self.module.log(
"PODMAN-CONTAINER-DEBUG: Error writing systemd: %s" % e)
return empty

def update_container_result(self, changed=True):
"""Inspect the current container, update results with last info, exit.
Expand All @@ -1578,7 +1529,7 @@ def update_container_result(self, changed=True):
if self.module.params['debug'] or self.module_params['debug']:
self.results.update({'podman_version': self.container.version})
self.results.update(
{'podman_systemd': self.generate_systemd()})
{'podman_systemd': generate_systemd(self.module, self.module_params, self.name)})

def make_started(self):
"""Run actions if desired state is 'started'."""
Expand Down
6 changes: 5 additions & 1 deletion plugins/module_utils/podman/podman_pod_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from ansible.module_utils._text import to_bytes, to_native

from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys
from ansible_collections.containers.podman.plugins.module_utils.podman.common import generate_systemd

__metaclass__ = type

Expand All @@ -28,6 +29,7 @@
dns=dict(type='list', elements='str', required=False),
dns_opt=dict(type='list', elements='str', required=False),
dns_search=dict(type='list', elements='str', required=False),
generate_systemd=dict(type='dict', default={}),
hostname=dict(type='str', required=False),
infra=dict(type='bool', required=False),
infra_conmon_pidfile=dict(type='str', required=False),
Expand Down Expand Up @@ -162,7 +164,7 @@ def addparam_ip(self, c):
def addparam_label(self, c):
for label in self.params['label'].items():
c += ['--label', b'='.join(
[to_bytes(l, errors='surrogate_or_strict') for l in label])]
[to_bytes(i, errors='surrogate_or_strict') for i in label])]
return c

def addparam_label_file(self, c):
Expand Down Expand Up @@ -644,6 +646,8 @@ def update_pod_result(self, changed=True):
self.results.update({'diff': self.pod.diff})
if self.module.params['debug'] or self.module_params['debug']:
self.results.update({'podman_version': self.pod.version})
self.results.update(
{'podman_systemd': generate_systemd(self.module, self.module_params, self.name)})

def execute(self):
"""Execute the desired action according to map of actions & states."""
Expand Down
3 changes: 2 additions & 1 deletion plugins/modules/podman_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@
suboptions:
file:
description:
- Specify a path to the service file to be generated.
- Specify a path to the directory where unit files will be generated.
If it doesn't exist, the directory will be created.
type: str
required: false
restart_policy:
Expand Down
60 changes: 60 additions & 0 deletions plugins/modules/podman_pod.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,66 @@
type: list
elements: str
required: false
generate_systemd:
description:
- Generate systemd unit file for container.
type: dict
default: {}
suboptions:
file:
description:
- Specify a path to the directory where unit files will be generated.
If it doesn't exist, the directory will be created.
type: str
required: false
restart_policy:
description:
- Specify a restart policy for the service. The restart-policy must be one of
"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always".
The default policy is "on-failure".
type: str
required: false
choices:
- 'no'
- 'on-success'
- 'on-failure'
- 'on-abnormal'
- 'on-watchdog'
- 'on-abort'
- 'always'
time:
description:
- Override the default stop timeout for the container with the given value.
type: int
required: false
no_header:
description:
- Do not generate the header including meta data such as the Podman version and the timestamp.
From podman version 3.1.0.
type: bool
default: false
names:
description:
- Use names of the containers for the start, stop, and description in the unit file.
Default is true.
type: bool
default: true
container_prefix:
description:
- Set the systemd unit name prefix for containers. The default is "container".
type: str
required: false
pod_prefix:
description:
- Set the systemd unit name prefix for pods. The default is "pod".
type: str
required: false
separator:
description:
- Set the systemd unit name separator between the name/id of a
container/pod and the prefix. The default is "-" (dash).
type: str
required: false
hostname:
description:
- Set a hostname to the pod
Expand Down
16 changes: 8 additions & 8 deletions tests/integration/targets/podman_container/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -533,8 +533,8 @@
assert:
that:
- idem4 is changed
- idem4.podman_systemd.name != ''
- idem4.podman_systemd.text != ''
- idem4.podman_systemd.keys() | list | length > 0
- idem4.podman_systemd.values() | list | length > 0

- name: Run container with systemd generation parameters
containers.podman.podman_container:
Expand All @@ -553,14 +553,14 @@
container_prefix: contain
register: system1

- name: Check that container is recreated when changed
- name: Check that container has correct systemd output
assert:
that:
- system1.podman_systemd.name == 'containzzzzcontainer1'
- system1.podman_systemd.text != ''
- "'stop -t 120 container1' in system1.podman_systemd.text"
- "'Restart=always' in system1.podman_systemd.text"
- "'autogenerated by Podman' not in system1.podman_systemd.text"
- system1.podman_systemd.keys() | list | first == 'containzzzzcontainer1'
- system1.podman_systemd.values() | list | length > 0
- "'stop -t 120 container1' in system1.podman_systemd.values() | list | first"
- "'Restart=always' in system1.podman_systemd.values() | list | first"
- "'autogenerated by Podman' not in system1.podman_systemd.values() | list | first"

always:

Expand Down
52 changes: 52 additions & 0 deletions tests/integration/targets/podman_pod/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,50 @@
that:
- pod35_info is not changed

- name: Start pod2 with default settings
containers.podman.podman_pod:
name: pod2
state: created
register: pod36_info

- name: Run container1 in pod
containers.podman.podman_container:
name: container1
image: alpine
command: top
pod: pod2
state: started

- name: Run container2 in pod
containers.podman.podman_container:
name: container2
image: alpine
command: top
pod: pod2
state: started

- name: Start pod2
containers.podman.podman_pod:
name: pod2
state: started
generate_systemd:
file: /tmp/dir1
restart_policy: always
time: 120
no_header: true
names: true
pod_prefix: poditto
container_prefix: ainer
register: system1

- name: Check that all settings from systemd are correct
assert:
that:
- system1.podman_systemd.keys() | list | length == 3
- "'stop -t 120 ' in system1.podman_systemd['poditto-pod2']"
- "'Restart=always' in system1.podman_systemd['poditto-pod2']"
- "'autogenerated by Podman' not in system1.podman_systemd['poditto-pod2']"

always:

- name: Delete all pods leftovers from tests
Expand All @@ -649,6 +693,14 @@
- "pod1"
- "pod2"

- name: Delete all container leftovers from tests
containers.podman.podman_container:
name: "{{ item }}"
state: absent
loop:
- "container1"
- "container2"

- name: Test idempotency for root pods
include_tasks: root-pod.yml
vars:
Expand Down