Skip to content

Commit

Permalink
Add systemd generation for pods
Browse files Browse the repository at this point in the history
  • Loading branch information
sshnaidm committed Sep 14, 2021
1 parent 24329ce commit 2f03aa3
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 60 deletions.
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 = {'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"]:
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
59 changes: 59 additions & 0 deletions plugins/modules/podman_pod.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,65 @@
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 service file to be generated.
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 | first != ''
- idem4.podman_systemd.values() | list | first != ''

- 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 | first != ''
- "'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

0 comments on commit 2f03aa3

Please sign in to comment.