mirror of
https://github.com/containers/ansible-podman-collections.git
synced 2026-02-03 23:01:48 +00:00
Run black -l 120 on all Python files to unify the style (#939)
Signed-off-by: Sagi Shnaidman <sshnaidm@redhat.com>
This commit is contained in:
parent
50c5a2549d
commit
4c682e170c
39 changed files with 3828 additions and 3129 deletions
|
|
@ -9,6 +9,6 @@ if len(sys.argv) < 2:
|
|||
version = sys.argv[1]
|
||||
with open("galaxy.yml.in") as f:
|
||||
y = yaml.safe_load(f)
|
||||
y['version'] = version
|
||||
y["version"] = version
|
||||
with open("galaxy.yml", "w") as ff:
|
||||
yaml.safe_dump(y, ff)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
# Copyright (c) 2022 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# Written by Janos Gerzson (grzs@backendo.com)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
|
|
@ -119,7 +120,7 @@ from ansible.plugins.become import BecomeBase
|
|||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'containers.podman.podman_unshare'
|
||||
name = "containers.podman.podman_unshare"
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
|
@ -127,18 +128,20 @@ class BecomeModule(BecomeBase):
|
|||
if not cmd:
|
||||
return cmd
|
||||
|
||||
becomecmd = 'podman unshare'
|
||||
becomecmd = "podman unshare"
|
||||
|
||||
user = self.get_option('become_user') or 'root'
|
||||
if user != 'root':
|
||||
cmdlist = [self.get_option('become_exe') or 'sudo']
|
||||
user = self.get_option("become_user") or "root"
|
||||
if user != "root":
|
||||
cmdlist = [self.get_option("become_exe") or "sudo"]
|
||||
# -i is required, because
|
||||
# podman unshare should be executed in a login shell to avoid chdir permission errors
|
||||
cmdlist.append('-iu %s' % user)
|
||||
if self.get_option('become_pass'):
|
||||
self.prompt = '[sudo podman unshare via ansible, key=%s] password:' % self._id
|
||||
cmdlist.append("-iu %s" % user)
|
||||
if self.get_option("become_pass"):
|
||||
self.prompt = (
|
||||
"[sudo podman unshare via ansible, key=%s] password:" % self._id
|
||||
)
|
||||
cmdlist.append('-p "%s"' % self.prompt)
|
||||
cmdlist.append('-- %s' % becomecmd)
|
||||
becomecmd = ' '.join(cmdlist)
|
||||
cmdlist.append("-- %s" % becomecmd)
|
||||
becomecmd = " ".join(cmdlist)
|
||||
|
||||
return ' '.join([becomecmd, self._build_success_command(cmd, shell)])
|
||||
return " ".join([becomecmd, self._build_success_command(cmd, shell)])
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@
|
|||
#
|
||||
# Written by: Tomas Tomecek (https://github.com/TomasTomecek)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
short_description: Interact with an existing buildah container
|
||||
description:
|
||||
- Run commands or put/fetch files to an existing container using buildah tool.
|
||||
|
|
@ -39,7 +40,7 @@ DOCUMENTATION = '''
|
|||
- name: ansible_user
|
||||
# keyword:
|
||||
# - name: remote_user
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
import shlex
|
||||
|
|
@ -61,7 +62,7 @@ class Connection(ConnectionBase):
|
|||
"""
|
||||
|
||||
# String used to identify this Connection class from other classes
|
||||
transport = 'containers.podman.buildah'
|
||||
transport = "containers.podman.buildah"
|
||||
has_pipelining = True
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
|
|
@ -77,7 +78,9 @@ class Connection(ConnectionBase):
|
|||
display.vvvv("Using buildah connection from collection")
|
||||
|
||||
def _set_user(self):
|
||||
self._buildah(b"config", [b"--user=" + to_bytes(self.user, errors='surrogate_or_strict')])
|
||||
self._buildah(
|
||||
b"config", [b"--user=" + to_bytes(self.user, errors="surrogate_or_strict")]
|
||||
)
|
||||
|
||||
def _buildah(self, cmd, cmd_args=None, in_data=None, outfile_stdout=None):
|
||||
"""
|
||||
|
|
@ -89,17 +92,17 @@ class Connection(ConnectionBase):
|
|||
:param outfile_stdout: file for writing STDOUT to
|
||||
:return: return code, stdout, stderr
|
||||
"""
|
||||
buildah_exec = 'buildah'
|
||||
buildah_exec = "buildah"
|
||||
local_cmd = [buildah_exec]
|
||||
|
||||
if isinstance(cmd, str):
|
||||
local_cmd.append(cmd)
|
||||
else:
|
||||
local_cmd.extend(cmd)
|
||||
if self.user and self.user != 'root':
|
||||
if cmd == 'run':
|
||||
if self.user and self.user != "root":
|
||||
if cmd == "run":
|
||||
local_cmd.extend(("--user", self.user))
|
||||
elif cmd == 'copy':
|
||||
elif cmd == "copy":
|
||||
local_cmd.extend(("--chown", self.user))
|
||||
local_cmd.append(self._container_id)
|
||||
|
||||
|
|
@ -109,23 +112,27 @@ class Connection(ConnectionBase):
|
|||
else:
|
||||
local_cmd.extend(cmd_args)
|
||||
|
||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict')
|
||||
for i in local_cmd]
|
||||
local_cmd = [to_bytes(i, errors="surrogate_or_strict") for i in local_cmd]
|
||||
|
||||
display.vvv("RUN %s" % (local_cmd,), host=self._container_id)
|
||||
if outfile_stdout:
|
||||
stdout_fd = open(outfile_stdout, "wb")
|
||||
else:
|
||||
stdout_fd = subprocess.PIPE
|
||||
p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE,
|
||||
stdout=stdout_fd, stderr=subprocess.PIPE)
|
||||
p = subprocess.Popen(
|
||||
local_cmd,
|
||||
shell=False,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=stdout_fd,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
||||
stdout, stderr = p.communicate(input=in_data)
|
||||
display.vvvv("STDOUT %s" % to_text(stdout))
|
||||
display.vvvv("STDERR %s" % to_text(stderr))
|
||||
display.vvvv("RC CODE %s" % p.returncode)
|
||||
stdout = to_bytes(stdout, errors='surrogate_or_strict')
|
||||
stderr = to_bytes(stderr, errors='surrogate_or_strict')
|
||||
stdout = to_bytes(stdout, errors="surrogate_or_strict")
|
||||
stderr = to_bytes(stderr, errors="surrogate_or_strict")
|
||||
return p.returncode, stdout, stderr
|
||||
|
||||
def _connect(self):
|
||||
|
|
@ -136,19 +143,26 @@ class Connection(ConnectionBase):
|
|||
super(Connection, self)._connect()
|
||||
rc, self._mount_point, stderr = self._buildah("mount")
|
||||
if rc != 0:
|
||||
display.v("Failed to mount container %s: %s" % (self._container_id, stderr.strip()))
|
||||
display.v(
|
||||
"Failed to mount container %s: %s"
|
||||
% (self._container_id, stderr.strip())
|
||||
)
|
||||
else:
|
||||
self._mount_point = self._mount_point.strip() + to_bytes(os.path.sep, errors='surrogate_or_strict')
|
||||
display.vvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr))
|
||||
self._mount_point = self._mount_point.strip() + to_bytes(
|
||||
os.path.sep, errors="surrogate_or_strict"
|
||||
)
|
||||
display.vvvv(
|
||||
"MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)
|
||||
)
|
||||
self._connected = True
|
||||
|
||||
@ensure_connect
|
||||
def exec_command(self, cmd, in_data=None, sudoable=False):
|
||||
""" run specified command in a running OCI container using buildah """
|
||||
"""run specified command in a running OCI container using buildah"""
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
|
||||
# shlex.split has a bug with text strings on Python-2.6 and can only handle text strings on Python-3
|
||||
cmd_args_list = shlex.split(to_native(cmd, errors='surrogate_or_strict'))
|
||||
cmd_args_list = shlex.split(to_native(cmd, errors="surrogate_or_strict"))
|
||||
|
||||
rc, stdout, stderr = self._buildah("run", cmd_args_list, in_data)
|
||||
|
||||
|
|
@ -156,47 +170,51 @@ class Connection(ConnectionBase):
|
|||
return rc, stdout, stderr
|
||||
|
||||
def put_file(self, in_path, out_path):
|
||||
""" Place a local file located in 'in_path' inside container at 'out_path' """
|
||||
"""Place a local file located in 'in_path' inside container at 'out_path'"""
|
||||
super(Connection, self).put_file(in_path, out_path)
|
||||
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._container_id)
|
||||
if not self._mount_point or self.user:
|
||||
rc, stdout, stderr = self._buildah(
|
||||
"copy", [in_path, out_path])
|
||||
rc, stdout, stderr = self._buildah("copy", [in_path, out_path])
|
||||
if rc != 0:
|
||||
raise AnsibleError(
|
||||
"Failed to copy file from %s to %s in container %s\n%s" % (
|
||||
in_path, out_path, self._container_id, stderr)
|
||||
"Failed to copy file from %s to %s in container %s\n%s"
|
||||
% (in_path, out_path, self._container_id, stderr)
|
||||
)
|
||||
else:
|
||||
real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict')
|
||||
real_out_path = self._mount_point + to_bytes(
|
||||
out_path, errors="surrogate_or_strict"
|
||||
)
|
||||
shutil.copyfile(
|
||||
to_bytes(in_path, errors='surrogate_or_strict'),
|
||||
to_bytes(real_out_path, errors='surrogate_or_strict')
|
||||
to_bytes(in_path, errors="surrogate_or_strict"),
|
||||
to_bytes(real_out_path, errors="surrogate_or_strict"),
|
||||
)
|
||||
|
||||
def fetch_file(self, in_path, out_path):
|
||||
""" obtain file specified via 'in_path' from the container and place it at 'out_path' """
|
||||
"""obtain file specified via 'in_path' from the container and place it at 'out_path'"""
|
||||
super(Connection, self).fetch_file(in_path, out_path)
|
||||
display.vvv("FETCH %s TO %s" %
|
||||
(in_path, out_path), host=self._container_id)
|
||||
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._container_id)
|
||||
if not self._mount_point:
|
||||
rc, stdout, stderr = self._buildah(
|
||||
"run",
|
||||
["cat", to_bytes(in_path, errors='surrogate_or_strict')],
|
||||
outfile_stdout=out_path)
|
||||
["cat", to_bytes(in_path, errors="surrogate_or_strict")],
|
||||
outfile_stdout=out_path,
|
||||
)
|
||||
if rc != 0:
|
||||
raise AnsibleError("Failed to fetch file from %s to %s from container %s\n%s" % (
|
||||
in_path, out_path, self._container_id, stderr))
|
||||
raise AnsibleError(
|
||||
"Failed to fetch file from %s to %s from container %s\n%s"
|
||||
% (in_path, out_path, self._container_id, stderr)
|
||||
)
|
||||
else:
|
||||
real_in_path = self._mount_point + \
|
||||
to_bytes(in_path, errors='surrogate_or_strict')
|
||||
real_in_path = self._mount_point + to_bytes(
|
||||
in_path, errors="surrogate_or_strict"
|
||||
)
|
||||
shutil.copyfile(
|
||||
to_bytes(real_in_path, errors='surrogate_or_strict'),
|
||||
to_bytes(out_path, errors='surrogate_or_strict')
|
||||
to_bytes(real_in_path, errors="surrogate_or_strict"),
|
||||
to_bytes(out_path, errors="surrogate_or_strict"),
|
||||
)
|
||||
|
||||
def close(self):
|
||||
""" unmount container's filesystem """
|
||||
"""unmount container's filesystem"""
|
||||
super(Connection, self).close()
|
||||
rc, stdout, stderr = self._buildah("umount")
|
||||
display.vvvv("RC %s STDOUT %r STDERR %r" % (rc, stdout, stderr))
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@
|
|||
#
|
||||
# Written by: Tomas Tomecek (https://github.com/TomasTomecek)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
author: Tomas Tomecek (@TomasTomecek)
|
||||
name: podman
|
||||
short_description: Interact with an existing podman container
|
||||
|
|
@ -56,7 +57,7 @@ DOCUMENTATION = '''
|
|||
- name: ansible_podman_executable
|
||||
env:
|
||||
- name: ANSIBLE_PODMAN_EXECUTABLE
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
import shlex
|
||||
|
|
@ -79,7 +80,7 @@ class Connection(ConnectionBase):
|
|||
"""
|
||||
|
||||
# String used to identify this Connection class from other classes
|
||||
transport = 'containers.podman.podman'
|
||||
transport = "containers.podman.podman"
|
||||
# We know that pipelining does not work with podman. Do not enable it, or
|
||||
# users will start containers and fail to connect to them.
|
||||
has_pipelining = False
|
||||
|
|
@ -104,7 +105,7 @@ class Connection(ConnectionBase):
|
|||
:param use_container_id: whether to append the container ID to the command
|
||||
:return: return code, stdout, stderr
|
||||
"""
|
||||
podman_exec = self.get_option('podman_executable')
|
||||
podman_exec = self.get_option("podman_executable")
|
||||
try:
|
||||
podman_cmd = get_bin_path(podman_exec)
|
||||
except ValueError:
|
||||
|
|
@ -112,11 +113,12 @@ class Connection(ConnectionBase):
|
|||
if not podman_cmd:
|
||||
raise AnsibleError("%s command not found in PATH" % podman_exec)
|
||||
local_cmd = [podman_cmd]
|
||||
if self.get_option('podman_extra_args'):
|
||||
if self.get_option("podman_extra_args"):
|
||||
local_cmd += shlex.split(
|
||||
to_native(
|
||||
self.get_option('podman_extra_args'),
|
||||
errors='surrogate_or_strict'))
|
||||
self.get_option("podman_extra_args"), errors="surrogate_or_strict"
|
||||
)
|
||||
)
|
||||
if isinstance(cmd, str):
|
||||
local_cmd.append(cmd)
|
||||
else:
|
||||
|
|
@ -126,18 +128,23 @@ class Connection(ConnectionBase):
|
|||
local_cmd.append(self._container_id)
|
||||
if cmd_args:
|
||||
local_cmd += cmd_args
|
||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
||||
local_cmd = [to_bytes(i, errors="surrogate_or_strict") for i in local_cmd]
|
||||
|
||||
display.vvv("RUN %s" % (local_cmd,), host=self._container_id)
|
||||
p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p = subprocess.Popen(
|
||||
local_cmd,
|
||||
shell=False,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
||||
stdout, stderr = p.communicate(input=in_data)
|
||||
display.vvvvv("STDOUT %s" % stdout)
|
||||
display.vvvvv("STDERR %s" % stderr)
|
||||
display.vvvvv("RC CODE %s" % p.returncode)
|
||||
stdout = to_bytes(stdout, errors='surrogate_or_strict')
|
||||
stderr = to_bytes(stderr, errors='surrogate_or_strict')
|
||||
stdout = to_bytes(stdout, errors="surrogate_or_strict")
|
||||
stderr = to_bytes(stderr, errors="surrogate_or_strict")
|
||||
return p.returncode, stdout, stderr
|
||||
|
||||
def _connect(self):
|
||||
|
|
@ -148,22 +155,30 @@ class Connection(ConnectionBase):
|
|||
super(Connection, self)._connect()
|
||||
rc, self._mount_point, stderr = self._podman("mount")
|
||||
if rc != 0:
|
||||
display.vvvv("Failed to mount container %s: %s" % (self._container_id, stderr.strip()))
|
||||
display.vvvv(
|
||||
"Failed to mount container %s: %s"
|
||||
% (self._container_id, stderr.strip())
|
||||
)
|
||||
elif not os.listdir(self._mount_point.strip()):
|
||||
display.vvvv("Failed to mount container with CGroups2: empty dir %s" % self._mount_point.strip())
|
||||
display.vvvv(
|
||||
"Failed to mount container with CGroups2: empty dir %s"
|
||||
% self._mount_point.strip()
|
||||
)
|
||||
self._mount_point = None
|
||||
else:
|
||||
self._mount_point = self._mount_point.strip()
|
||||
display.vvvvv("MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr))
|
||||
display.vvvvv(
|
||||
"MOUNTPOINT %s RC %s STDERR %r" % (self._mount_point, rc, stderr)
|
||||
)
|
||||
self._connected = True
|
||||
|
||||
@ensure_connect
|
||||
def exec_command(self, cmd, in_data=None, sudoable=False):
|
||||
""" run specified command in a running OCI container using podman """
|
||||
"""run specified command in a running OCI container using podman"""
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
|
||||
# shlex.split has a bug with text strings on Python-2.6 and can only handle text strings on Python-3
|
||||
cmd_args_list = shlex.split(to_native(cmd, errors='surrogate_or_strict'))
|
||||
cmd_args_list = shlex.split(to_native(cmd, errors="surrogate_or_strict"))
|
||||
exec_args_list = ["exec"]
|
||||
if self.user:
|
||||
exec_args_list.extend(("--user", self.user))
|
||||
|
|
@ -174,56 +189,70 @@ class Connection(ConnectionBase):
|
|||
return rc, stdout, stderr
|
||||
|
||||
def put_file(self, in_path, out_path):
|
||||
""" Place a local file located in 'in_path' inside container at 'out_path' """
|
||||
"""Place a local file located in 'in_path' inside container at 'out_path'"""
|
||||
super(Connection, self).put_file(in_path, out_path)
|
||||
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._container_id)
|
||||
if not self._mount_point or self.user:
|
||||
rc, stdout, stderr = self._podman(
|
||||
"cp", [in_path, self._container_id + ":" + out_path], use_container_id=False
|
||||
"cp",
|
||||
[in_path, self._container_id + ":" + out_path],
|
||||
use_container_id=False,
|
||||
)
|
||||
if rc != 0:
|
||||
rc, stdout, stderr = self._podman(
|
||||
"cp", ["--pause=false", in_path, self._container_id + ":" + out_path], use_container_id=False
|
||||
"cp",
|
||||
["--pause=false", in_path, self._container_id + ":" + out_path],
|
||||
use_container_id=False,
|
||||
)
|
||||
if rc != 0:
|
||||
raise AnsibleError(
|
||||
"Failed to copy file from %s to %s in container %s\n%s" % (
|
||||
in_path, out_path, self._container_id, stderr)
|
||||
"Failed to copy file from %s to %s in container %s\n%s"
|
||||
% (in_path, out_path, self._container_id, stderr)
|
||||
)
|
||||
if self.user:
|
||||
rc, stdout, stderr = self._podman(
|
||||
"exec", ["chown", self.user, out_path])
|
||||
"exec", ["chown", self.user, out_path]
|
||||
)
|
||||
if rc != 0:
|
||||
raise AnsibleError(
|
||||
"Failed to chown file %s for user %s in container %s\n%s" % (
|
||||
out_path, self.user, self._container_id, stderr)
|
||||
"Failed to chown file %s for user %s in container %s\n%s"
|
||||
% (out_path, self.user, self._container_id, stderr)
|
||||
)
|
||||
else:
|
||||
real_out_path = self._mount_point + to_bytes(out_path, errors='surrogate_or_strict')
|
||||
real_out_path = self._mount_point + to_bytes(
|
||||
out_path, errors="surrogate_or_strict"
|
||||
)
|
||||
shutil.copyfile(
|
||||
to_bytes(in_path, errors='surrogate_or_strict'),
|
||||
to_bytes(real_out_path, errors='surrogate_or_strict')
|
||||
to_bytes(in_path, errors="surrogate_or_strict"),
|
||||
to_bytes(real_out_path, errors="surrogate_or_strict"),
|
||||
)
|
||||
|
||||
def fetch_file(self, in_path, out_path):
|
||||
""" obtain file specified via 'in_path' from the container and place it at 'out_path' """
|
||||
"""obtain file specified via 'in_path' from the container and place it at 'out_path'"""
|
||||
super(Connection, self).fetch_file(in_path, out_path)
|
||||
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._container_id)
|
||||
if not self._mount_point:
|
||||
rc, stdout, stderr = self._podman(
|
||||
"cp", [self._container_id + ":" + in_path, out_path], use_container_id=False)
|
||||
"cp",
|
||||
[self._container_id + ":" + in_path, out_path],
|
||||
use_container_id=False,
|
||||
)
|
||||
if rc != 0:
|
||||
raise AnsibleError("Failed to fetch file from %s to %s from container %s\n%s" % (
|
||||
in_path, out_path, self._container_id, stderr))
|
||||
raise AnsibleError(
|
||||
"Failed to fetch file from %s to %s from container %s\n%s"
|
||||
% (in_path, out_path, self._container_id, stderr)
|
||||
)
|
||||
else:
|
||||
real_in_path = self._mount_point + to_bytes(in_path, errors='surrogate_or_strict')
|
||||
real_in_path = self._mount_point + to_bytes(
|
||||
in_path, errors="surrogate_or_strict"
|
||||
)
|
||||
shutil.copyfile(
|
||||
to_bytes(real_in_path, errors='surrogate_or_strict'),
|
||||
to_bytes(out_path, errors='surrogate_or_strict')
|
||||
to_bytes(real_in_path, errors="surrogate_or_strict"),
|
||||
to_bytes(out_path, errors="surrogate_or_strict"),
|
||||
)
|
||||
|
||||
def close(self):
|
||||
""" unmount container's filesystem """
|
||||
"""unmount container's filesystem"""
|
||||
super(Connection, self).close()
|
||||
# we actually don't need to unmount since the container is mounted anyway
|
||||
# rc, stdout, stderr = self._podman("umount")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# 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
|
||||
|
||||
import json
|
||||
|
|
@ -9,37 +10,45 @@ import os
|
|||
import shutil
|
||||
|
||||
from ansible.module_utils.six import raise_from
|
||||
|
||||
try:
|
||||
from ansible.module_utils.compat.version import LooseVersion # noqa: F401
|
||||
except ImportError:
|
||||
try:
|
||||
from distutils.version import LooseVersion # noqa: F401
|
||||
except ImportError as exc:
|
||||
raise_from(ImportError('To use this plugin or module with ansible-core'
|
||||
' < 2.11, you need to use Python < 3.12 with '
|
||||
'distutils.version present'), exc)
|
||||
raise_from(
|
||||
ImportError(
|
||||
"To use this plugin or module with ansible-core"
|
||||
" < 2.11, you need to use Python < 3.12 with "
|
||||
"distutils.version present"
|
||||
),
|
||||
exc,
|
||||
)
|
||||
|
||||
ARGUMENTS_OPTS_DICT = {
|
||||
'--attach': ['--attach', '-a'],
|
||||
'--cpu-shares': ['--cpu-shares', '-c'],
|
||||
'--detach': ['--detach', '-d'],
|
||||
'--env': ['--env', '-e'],
|
||||
'--hostname': ['--hostname', '-h'],
|
||||
'--interactive': ['--interactive', '-i'],
|
||||
'--label': ['--label', '-l'],
|
||||
'--memory': ['--memory', '-m'],
|
||||
'--network': ['--network', '--net'],
|
||||
'--publish': ['--publish', '-p'],
|
||||
'--publish-all': ['--publish-all', '-P'],
|
||||
'--quiet': ['--quiet', '-q'],
|
||||
'--tty': ['--tty', '-t'],
|
||||
'--user': ['--user', '-u'],
|
||||
'--volume': ['--volume', '-v'],
|
||||
'--workdir': ['--workdir', '-w'],
|
||||
"--attach": ["--attach", "-a"],
|
||||
"--cpu-shares": ["--cpu-shares", "-c"],
|
||||
"--detach": ["--detach", "-d"],
|
||||
"--env": ["--env", "-e"],
|
||||
"--hostname": ["--hostname", "-h"],
|
||||
"--interactive": ["--interactive", "-i"],
|
||||
"--label": ["--label", "-l"],
|
||||
"--memory": ["--memory", "-m"],
|
||||
"--network": ["--network", "--net"],
|
||||
"--publish": ["--publish", "-p"],
|
||||
"--publish-all": ["--publish-all", "-P"],
|
||||
"--quiet": ["--quiet", "-q"],
|
||||
"--tty": ["--tty", "-t"],
|
||||
"--user": ["--user", "-u"],
|
||||
"--volume": ["--volume", "-v"],
|
||||
"--workdir": ["--workdir", "-w"],
|
||||
}
|
||||
|
||||
|
||||
def run_podman_command(module, executable='podman', args=None, expected_rc=0, ignore_errors=False):
|
||||
def run_podman_command(
|
||||
module, executable="podman", args=None, expected_rc=0, ignore_errors=False
|
||||
):
|
||||
if not isinstance(executable, list):
|
||||
command = [executable]
|
||||
if args is not None:
|
||||
|
|
@ -47,77 +56,94 @@ def run_podman_command(module, executable='podman', args=None, expected_rc=0, ig
|
|||
rc, out, err = module.run_command(command)
|
||||
if not ignore_errors and rc != expected_rc:
|
||||
module.fail_json(
|
||||
msg='Failed to run {command} {args}: {err}'.format(
|
||||
command=command, args=args, err=err))
|
||||
msg="Failed to run {command} {args}: {err}".format(
|
||||
command=command, args=args, err=err
|
||||
)
|
||||
)
|
||||
return rc, out, err
|
||||
|
||||
|
||||
def run_generate_systemd_command(module, module_params, name, version):
|
||||
"""Generate systemd unit file."""
|
||||
command = [module_params['executable'], 'generate', 'systemd',
|
||||
name, '--format', 'json']
|
||||
sysconf = module_params['generate_systemd']
|
||||
gt4ver = LooseVersion(version) >= LooseVersion('4.0.0')
|
||||
if sysconf.get('restart_policy'):
|
||||
if sysconf.get('restart_policy') not in [
|
||||
"no", "on-success", "on-failure", "on-abnormal", "on-watchdog",
|
||||
"on-abort", "always"]:
|
||||
command = [
|
||||
module_params["executable"],
|
||||
"generate",
|
||||
"systemd",
|
||||
name,
|
||||
"--format",
|
||||
"json",
|
||||
]
|
||||
sysconf = module_params["generate_systemd"]
|
||||
gt4ver = LooseVersion(version) >= LooseVersion("4.0.0")
|
||||
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('restart_sec') is not None:
|
||||
command.extend(['--restart-sec=%s' % sysconf['restart_sec']])
|
||||
if (sysconf.get('stop_timeout') is not None) or (sysconf.get('time') is not None):
|
||||
'"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("restart_sec") is not None:
|
||||
command.extend(["--restart-sec=%s" % sysconf["restart_sec"]])
|
||||
if (sysconf.get("stop_timeout") is not None) or (sysconf.get("time") is not None):
|
||||
# Select correct parameter name based on version
|
||||
arg_name = 'stop-timeout' if gt4ver else 'time'
|
||||
arg_value = sysconf.get('stop_timeout') if sysconf.get('stop_timeout') is not None else sysconf.get('time')
|
||||
command.extend(['--%s=%s' % (arg_name, arg_value)])
|
||||
if sysconf.get('start_timeout') is not None:
|
||||
command.extend(['--start-timeout=%s' % sysconf['start_timeout']])
|
||||
if sysconf.get('no_header'):
|
||||
command.extend(['--no-header'])
|
||||
if sysconf.get('names', True):
|
||||
command.extend(['--name'])
|
||||
arg_name = "stop-timeout" if gt4ver else "time"
|
||||
arg_value = (
|
||||
sysconf.get("stop_timeout")
|
||||
if sysconf.get("stop_timeout") is not None
|
||||
else sysconf.get("time")
|
||||
)
|
||||
command.extend(["--%s=%s" % (arg_name, arg_value)])
|
||||
if sysconf.get("start_timeout") is not None:
|
||||
command.extend(["--start-timeout=%s" % sysconf["start_timeout"]])
|
||||
if sysconf.get("no_header"):
|
||||
command.extend(["--no-header"])
|
||||
if sysconf.get("names", True):
|
||||
command.extend(["--name"])
|
||||
if sysconf.get("new"):
|
||||
command.extend(["--new"])
|
||||
if sysconf.get('container_prefix') is not None:
|
||||
command.extend(['--container-prefix=%s' % sysconf['container_prefix']])
|
||||
if sysconf.get('pod_prefix') is not None:
|
||||
command.extend(['--pod-prefix=%s' % sysconf['pod_prefix']])
|
||||
if sysconf.get('separator') is not None:
|
||||
command.extend(['--separator=%s' % sysconf['separator']])
|
||||
if sysconf.get('after') is not None:
|
||||
if sysconf.get("container_prefix") is not None:
|
||||
command.extend(["--container-prefix=%s" % sysconf["container_prefix"]])
|
||||
if sysconf.get("pod_prefix") is not None:
|
||||
command.extend(["--pod-prefix=%s" % sysconf["pod_prefix"]])
|
||||
if sysconf.get("separator") is not None:
|
||||
command.extend(["--separator=%s" % sysconf["separator"]])
|
||||
if sysconf.get("after") is not None:
|
||||
|
||||
sys_after = sysconf['after']
|
||||
sys_after = sysconf["after"]
|
||||
if isinstance(sys_after, str):
|
||||
sys_after = [sys_after]
|
||||
for after in sys_after:
|
||||
command.extend(['--after=%s' % after])
|
||||
if sysconf.get('wants') is not None:
|
||||
sys_wants = sysconf['wants']
|
||||
command.extend(["--after=%s" % after])
|
||||
if sysconf.get("wants") is not None:
|
||||
sys_wants = sysconf["wants"]
|
||||
if isinstance(sys_wants, str):
|
||||
sys_wants = [sys_wants]
|
||||
for want in sys_wants:
|
||||
command.extend(['--wants=%s' % want])
|
||||
if sysconf.get('requires') is not None:
|
||||
sys_req = sysconf['requires']
|
||||
command.extend(["--wants=%s" % want])
|
||||
if sysconf.get("requires") is not None:
|
||||
sys_req = sysconf["requires"]
|
||||
if isinstance(sys_req, str):
|
||||
sys_req = [sys_req]
|
||||
for require in sys_req:
|
||||
command.extend(['--requires=%s' % require])
|
||||
for param in ['after', 'wants', 'requires']:
|
||||
command.extend(["--requires=%s" % require])
|
||||
for param in ["after", "wants", "requires"]:
|
||||
if sysconf.get(param) is not None and not gt4ver:
|
||||
module.fail_json(msg="Systemd parameter '%s' is supported from "
|
||||
"podman version 4 only! Current version is %s" % (
|
||||
param, version))
|
||||
module.fail_json(
|
||||
msg="Systemd parameter '%s' is supported from "
|
||||
"podman version 4 only! Current version is %s" % (param, version)
|
||||
)
|
||||
|
||||
if module.params['debug'] or module_params['debug']:
|
||||
module.log("PODMAN-CONTAINER-DEBUG: systemd command: %s" %
|
||||
" ".join(command))
|
||||
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)
|
||||
return rc, systemd, err
|
||||
|
||||
|
|
@ -125,14 +151,16 @@ def run_generate_systemd_command(module, module_params, name, version):
|
|||
def compare_systemd_file_content(file_path, file_content):
|
||||
if not os.path.exists(file_path):
|
||||
# File does not exist, so all lines in file_content are different
|
||||
return '', file_content
|
||||
return "", file_content
|
||||
# Read the file
|
||||
with open(file_path, 'r') as unit_file:
|
||||
with open(file_path, "r") as unit_file:
|
||||
current_unit_file_content = unit_file.read()
|
||||
|
||||
# Function to remove comments from file content
|
||||
def remove_comments(content):
|
||||
return "\n".join([line for line in content.splitlines() if not line.startswith('#')])
|
||||
return "\n".join(
|
||||
[line for line in content.splitlines() if not line.startswith("#")]
|
||||
)
|
||||
|
||||
# Remove comments from both file contents before comparison
|
||||
current_unit_file_content_nocmnt = remove_comments(current_unit_file_content)
|
||||
|
|
@ -141,111 +169,145 @@ def compare_systemd_file_content(file_path, file_content):
|
|||
return None
|
||||
|
||||
# Get the different lines between the two contents
|
||||
diff_in_file = [line
|
||||
for line in unit_content_nocmnt.splitlines()
|
||||
if line not in current_unit_file_content_nocmnt.splitlines()]
|
||||
diff_in_string = [line
|
||||
for line in current_unit_file_content_nocmnt.splitlines()
|
||||
if line not in unit_content_nocmnt.splitlines()]
|
||||
diff_in_file = [
|
||||
line
|
||||
for line in unit_content_nocmnt.splitlines()
|
||||
if line not in current_unit_file_content_nocmnt.splitlines()
|
||||
]
|
||||
diff_in_string = [
|
||||
line
|
||||
for line in current_unit_file_content_nocmnt.splitlines()
|
||||
if line not in unit_content_nocmnt.splitlines()
|
||||
]
|
||||
|
||||
return diff_in_string, diff_in_file
|
||||
|
||||
|
||||
def generate_systemd(module, module_params, name, version):
|
||||
result = {
|
||||
'changed': False,
|
||||
'systemd': {},
|
||||
'diff': {},
|
||||
"changed": False,
|
||||
"systemd": {},
|
||||
"diff": {},
|
||||
}
|
||||
sysconf = module_params['generate_systemd']
|
||||
rc, systemd, err = run_generate_systemd_command(module, module_params, name, version)
|
||||
sysconf = module_params["generate_systemd"]
|
||||
rc, systemd, err = run_generate_systemd_command(
|
||||
module, module_params, name, version
|
||||
)
|
||||
if rc != 0:
|
||||
module.log(
|
||||
"PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err)
|
||||
module.log("PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err)
|
||||
if sysconf:
|
||||
module.fail_json(msg="Error generating systemd: %s" % err)
|
||||
return result
|
||||
else:
|
||||
try:
|
||||
data = json.loads(systemd)
|
||||
result['systemd'] = data
|
||||
if sysconf.get('path'):
|
||||
full_path = os.path.expanduser(sysconf['path'])
|
||||
result["systemd"] = data
|
||||
if sysconf.get("path"):
|
||||
full_path = os.path.expanduser(sysconf["path"])
|
||||
if not os.path.exists(full_path):
|
||||
os.makedirs(full_path)
|
||||
result['changed'] = True
|
||||
result["changed"] = True
|
||||
if not os.path.isdir(full_path):
|
||||
module.fail_json("Path %s is not a directory! "
|
||||
"Can not save systemd unit files there!"
|
||||
% full_path)
|
||||
module.fail_json(
|
||||
"Path %s is not a directory! "
|
||||
"Can not save systemd unit files there!" % full_path
|
||||
)
|
||||
for file_name, file_content in data.items():
|
||||
file_name += ".service"
|
||||
if not os.path.exists(os.path.join(full_path, file_name)):
|
||||
result['changed'] = True
|
||||
if result['diff'].get('before') is None:
|
||||
result['diff'] = {'before': {}, 'after': {}}
|
||||
result['diff']['before'].update(
|
||||
{'systemd_{file_name}.service'.format(file_name=file_name): ''})
|
||||
result['diff']['after'].update(
|
||||
{'systemd_{file_name}.service'.format(file_name=file_name): file_content})
|
||||
result["changed"] = True
|
||||
if result["diff"].get("before") is None:
|
||||
result["diff"] = {"before": {}, "after": {}}
|
||||
result["diff"]["before"].update(
|
||||
{
|
||||
"systemd_{file_name}.service".format(
|
||||
file_name=file_name
|
||||
): ""
|
||||
}
|
||||
)
|
||||
result["diff"]["after"].update(
|
||||
{
|
||||
"systemd_{file_name}.service".format(
|
||||
file_name=file_name
|
||||
): file_content
|
||||
}
|
||||
)
|
||||
|
||||
else:
|
||||
diff_ = compare_systemd_file_content(os.path.join(full_path, file_name), file_content)
|
||||
diff_ = compare_systemd_file_content(
|
||||
os.path.join(full_path, file_name), file_content
|
||||
)
|
||||
if diff_:
|
||||
result['changed'] = True
|
||||
if result['diff'].get('before') is None:
|
||||
result['diff'] = {'before': {}, 'after': {}}
|
||||
result['diff']['before'].update(
|
||||
{'systemd_{file_name}.service'.format(file_name=file_name): "\n".join(diff_[0])})
|
||||
result['diff']['after'].update(
|
||||
{'systemd_{file_name}.service'.format(file_name=file_name): "\n".join(diff_[1])})
|
||||
with open(os.path.join(full_path, file_name), 'w') as f:
|
||||
result["changed"] = True
|
||||
if result["diff"].get("before") is None:
|
||||
result["diff"] = {"before": {}, "after": {}}
|
||||
result["diff"]["before"].update(
|
||||
{
|
||||
"systemd_{file_name}.service".format(
|
||||
file_name=file_name
|
||||
): "\n".join(diff_[0])
|
||||
}
|
||||
)
|
||||
result["diff"]["after"].update(
|
||||
{
|
||||
"systemd_{file_name}.service".format(
|
||||
file_name=file_name
|
||||
): "\n".join(diff_[1])
|
||||
}
|
||||
)
|
||||
with open(os.path.join(full_path, file_name), "w") as f:
|
||||
f.write(file_content)
|
||||
diff_before = "\n".join(
|
||||
["{j} - {k}".format(j=j, k=k)
|
||||
for j, k in result['diff'].get('before', {}).items() if 'PIDFile' not in k]).strip()
|
||||
[
|
||||
"{j} - {k}".format(j=j, k=k)
|
||||
for j, k in result["diff"].get("before", {}).items()
|
||||
if "PIDFile" not in k
|
||||
]
|
||||
).strip()
|
||||
diff_after = "\n".join(
|
||||
["{j} - {k}".format(j=j, k=k)
|
||||
for j, k in result['diff'].get('after', {}).items() if 'PIDFile' not in k]).strip()
|
||||
[
|
||||
"{j} - {k}".format(j=j, k=k)
|
||||
for j, k in result["diff"].get("after", {}).items()
|
||||
if "PIDFile" not in k
|
||||
]
|
||||
).strip()
|
||||
if diff_before or diff_after:
|
||||
result['diff']['before'] = diff_before + "\n"
|
||||
result['diff']['after'] = diff_after + "\n"
|
||||
result["diff"]["before"] = diff_before + "\n"
|
||||
result["diff"]["after"] = diff_after + "\n"
|
||||
else:
|
||||
result['diff'] = {}
|
||||
result["diff"] = {}
|
||||
return result
|
||||
except Exception as e:
|
||||
module.log(
|
||||
"PODMAN-CONTAINER-DEBUG: Error writing systemd: %s" % e)
|
||||
module.log("PODMAN-CONTAINER-DEBUG: Error writing systemd: %s" % e)
|
||||
if sysconf:
|
||||
module.fail_json(msg="Error writing systemd: %s" % e)
|
||||
return result
|
||||
|
||||
|
||||
def delete_systemd(module, module_params, name, version):
|
||||
sysconf = module_params['generate_systemd']
|
||||
if not sysconf.get('path'):
|
||||
sysconf = module_params["generate_systemd"]
|
||||
if not sysconf.get("path"):
|
||||
# We don't know where systemd files are located, nothing to delete
|
||||
module.log(
|
||||
"PODMAN-CONTAINER-DEBUG: Not deleting systemd file - no path!")
|
||||
module.log("PODMAN-CONTAINER-DEBUG: Not deleting systemd file - no path!")
|
||||
return
|
||||
rc, systemd, err = run_generate_systemd_command(module, module_params, name, version)
|
||||
rc, systemd, err = run_generate_systemd_command(
|
||||
module, module_params, name, version
|
||||
)
|
||||
if rc != 0:
|
||||
module.log(
|
||||
"PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err)
|
||||
module.log("PODMAN-CONTAINER-DEBUG: Error generating systemd: %s" % err)
|
||||
return
|
||||
else:
|
||||
try:
|
||||
data = json.loads(systemd)
|
||||
for file_name in data.keys():
|
||||
file_name += ".service"
|
||||
full_dir_path = os.path.expanduser(sysconf['path'])
|
||||
full_dir_path = os.path.expanduser(sysconf["path"])
|
||||
file_path = os.path.join(full_dir_path, file_name)
|
||||
if os.path.exists(file_path):
|
||||
os.unlink(file_path)
|
||||
return
|
||||
except Exception as e:
|
||||
module.log(
|
||||
"PODMAN-CONTAINER-DEBUG: Error deleting systemd: %s" % e)
|
||||
module.log("PODMAN-CONTAINER-DEBUG: Error deleting systemd: %s" % e)
|
||||
return
|
||||
|
||||
|
||||
|
|
@ -306,12 +368,12 @@ _signal_map = {
|
|||
"VTALRM": 26,
|
||||
"WINCH": 28,
|
||||
"XCPU": 24,
|
||||
"XFSZ": 25
|
||||
"XFSZ": 25,
|
||||
}
|
||||
|
||||
for i in range(1, _signal_map['RTMAX'] - _signal_map['RTMIN'] + 1):
|
||||
_signal_map['RTMIN+{0}'.format(i)] = _signal_map['RTMIN'] + i
|
||||
_signal_map['RTMAX-{0}'.format(i)] = _signal_map['RTMAX'] - i
|
||||
for i in range(1, _signal_map["RTMAX"] - _signal_map["RTMIN"] + 1):
|
||||
_signal_map["RTMIN+{0}".format(i)] = _signal_map["RTMIN"] + i
|
||||
_signal_map["RTMAX-{0}".format(i)] = _signal_map["RTMAX"] - i
|
||||
|
||||
|
||||
def normalize_signal(signal_name_or_number):
|
||||
|
|
@ -320,7 +382,7 @@ def normalize_signal(signal_name_or_number):
|
|||
return signal_name_or_number
|
||||
else:
|
||||
signal_name = signal_name_or_number.upper()
|
||||
if signal_name.startswith('SIG'):
|
||||
if signal_name.startswith("SIG"):
|
||||
signal_name = signal_name[3:]
|
||||
if signal_name not in _signal_map:
|
||||
raise RuntimeError("Unknown signal '{0}'".format(signal_name_or_number))
|
||||
|
|
@ -328,13 +390,15 @@ def normalize_signal(signal_name_or_number):
|
|||
|
||||
|
||||
def get_podman_version(module, fail=True):
|
||||
executable = module.params['executable'] if module.params['executable'] else 'podman'
|
||||
rc, out, err = module.run_command(
|
||||
[executable, b'--version'])
|
||||
executable = (
|
||||
module.params["executable"] if module.params["executable"] else "podman"
|
||||
)
|
||||
rc, out, err = module.run_command([executable, b"--version"])
|
||||
if rc != 0 or not out or "version" not in out:
|
||||
if fail:
|
||||
module.fail_json(msg="'%s --version' run failed! Error: %s" %
|
||||
(executable, err))
|
||||
module.fail_json(
|
||||
msg="'%s --version' run failed! Error: %s" % (executable, err)
|
||||
)
|
||||
return None
|
||||
return out.split("version")[1].strip()
|
||||
|
||||
|
|
@ -409,16 +473,23 @@ def diff_generic(params, info_config, module_arg, cmd_arg, boolean_type=False):
|
|||
if before:
|
||||
before = ",".join(sorted([str(i).lower() for i in before]))
|
||||
else:
|
||||
before = ''
|
||||
before = ""
|
||||
elif isinstance(after, dict):
|
||||
if module_arg == "log_opt" and "max_size" in after:
|
||||
after["max-size"] = after.pop("max_size")
|
||||
after = ",".join(sorted(
|
||||
[str(k).lower() + "=" + str(v).lower() for k, v in after.items() if v is not None]))
|
||||
after = ",".join(
|
||||
sorted(
|
||||
[
|
||||
str(k).lower() + "=" + str(v).lower()
|
||||
for k, v in after.items()
|
||||
if v is not None
|
||||
]
|
||||
)
|
||||
)
|
||||
if before:
|
||||
before = ",".join(sorted([j.lower() for j in before]))
|
||||
else:
|
||||
before = ''
|
||||
before = ""
|
||||
elif isinstance(after, bool):
|
||||
after = str(after).capitalize()
|
||||
if before is not None:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -2,12 +2,15 @@
|
|||
# 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
|
||||
|
||||
import os
|
||||
import shlex
|
||||
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import compare_systemd_file_content
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
compare_systemd_file_content,
|
||||
)
|
||||
|
||||
QUADLET_ROOT_PATH = "/etc/containers/systemd/"
|
||||
QUADLET_NON_ROOT_PATH = "~/.config/containers/systemd/"
|
||||
|
|
@ -52,102 +55,107 @@ class Quadlet:
|
|||
Construct the quadlet content as a string.
|
||||
"""
|
||||
custom_user_options = self.custom_params.get("quadlet_options")
|
||||
custom_text = "\n" + "\n".join(custom_user_options) if custom_user_options else ""
|
||||
return f"[{self.section}]\n" + "\n".join(
|
||||
f"{key}={value}" for key, value in self.dict_params
|
||||
) + custom_text + "\n"
|
||||
custom_text = (
|
||||
"\n" + "\n".join(custom_user_options) if custom_user_options else ""
|
||||
)
|
||||
return (
|
||||
f"[{self.section}]\n"
|
||||
+ "\n".join(f"{key}={value}" for key, value in self.dict_params)
|
||||
+ custom_text
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
def write_to_file(self, path: str):
|
||||
"""
|
||||
Write the quadlet content to a file at the specified path.
|
||||
"""
|
||||
content = self.create_quadlet_content()
|
||||
with open(path, 'w') as file:
|
||||
with open(path, "w") as file:
|
||||
file.write(content)
|
||||
|
||||
|
||||
class ContainerQuadlet(Quadlet):
|
||||
param_map = {
|
||||
'cap_add': 'AddCapability',
|
||||
'device': 'AddDevice',
|
||||
'annotation': 'Annotation',
|
||||
'name': 'ContainerName',
|
||||
"cap_add": "AddCapability",
|
||||
"device": "AddDevice",
|
||||
"annotation": "Annotation",
|
||||
"name": "ContainerName",
|
||||
# the following are not implemented yet in Podman module
|
||||
'AutoUpdate': 'AutoUpdate',
|
||||
'ContainersConfModule': 'ContainersConfModule',
|
||||
"AutoUpdate": "AutoUpdate",
|
||||
"ContainersConfModule": "ContainersConfModule",
|
||||
# end of not implemented yet
|
||||
'dns': 'DNS',
|
||||
'dns_option': 'DNSOption',
|
||||
'dns_search': 'DNSSearch',
|
||||
'cap_drop': 'DropCapability',
|
||||
'cgroups': 'CgroupsMode',
|
||||
'entrypoint': 'Entrypoint',
|
||||
'env': 'Environment',
|
||||
'env_file': 'EnvironmentFile',
|
||||
'env_host': 'EnvironmentHost',
|
||||
'etc_hosts': 'AddHost',
|
||||
'command': 'Exec',
|
||||
'expose': 'ExposeHostPort',
|
||||
'gidmap': 'GIDMap',
|
||||
'global_args': 'GlobalArgs',
|
||||
'group': 'Group', # Does not exist in module parameters
|
||||
'group_add': 'GroupAdd',
|
||||
'healthcheck': 'HealthCmd',
|
||||
'healthcheck_interval': 'HealthInterval',
|
||||
'healthcheck_failure_action': 'HealthOnFailure',
|
||||
'healthcheck_retries': 'HealthRetries',
|
||||
'healthcheck_start_period': 'HealthStartPeriod',
|
||||
'healthcheck_timeout': 'HealthTimeout',
|
||||
'health_startup_cmd': 'HealthStartupCmd',
|
||||
'health_startup_interval': 'HealthStartupInterval',
|
||||
'health_startup_retries': 'HealthStartupRetries',
|
||||
'health_startup_success': 'HealthStartupSuccess',
|
||||
'health_startup_timeout': 'HealthStartupTimeout',
|
||||
'hostname': 'HostName',
|
||||
'image': 'Image',
|
||||
'ip': 'IP',
|
||||
'ip6': 'IP6',
|
||||
'label': 'Label',
|
||||
'log_driver': 'LogDriver',
|
||||
'log_opt': 'LogOpt',
|
||||
"dns": "DNS",
|
||||
"dns_option": "DNSOption",
|
||||
"dns_search": "DNSSearch",
|
||||
"cap_drop": "DropCapability",
|
||||
"cgroups": "CgroupsMode",
|
||||
"entrypoint": "Entrypoint",
|
||||
"env": "Environment",
|
||||
"env_file": "EnvironmentFile",
|
||||
"env_host": "EnvironmentHost",
|
||||
"etc_hosts": "AddHost",
|
||||
"command": "Exec",
|
||||
"expose": "ExposeHostPort",
|
||||
"gidmap": "GIDMap",
|
||||
"global_args": "GlobalArgs",
|
||||
"group": "Group", # Does not exist in module parameters
|
||||
"group_add": "GroupAdd",
|
||||
"healthcheck": "HealthCmd",
|
||||
"healthcheck_interval": "HealthInterval",
|
||||
"healthcheck_failure_action": "HealthOnFailure",
|
||||
"healthcheck_retries": "HealthRetries",
|
||||
"healthcheck_start_period": "HealthStartPeriod",
|
||||
"healthcheck_timeout": "HealthTimeout",
|
||||
"health_startup_cmd": "HealthStartupCmd",
|
||||
"health_startup_interval": "HealthStartupInterval",
|
||||
"health_startup_retries": "HealthStartupRetries",
|
||||
"health_startup_success": "HealthStartupSuccess",
|
||||
"health_startup_timeout": "HealthStartupTimeout",
|
||||
"hostname": "HostName",
|
||||
"image": "Image",
|
||||
"ip": "IP",
|
||||
"ip6": "IP6",
|
||||
"label": "Label",
|
||||
"log_driver": "LogDriver",
|
||||
"log_opt": "LogOpt",
|
||||
"Mask": "Mask", # add it in security_opt
|
||||
'mount': 'Mount',
|
||||
'network': 'Network',
|
||||
'network_aliases': 'NetworkAlias',
|
||||
'no_new_privileges': 'NoNewPrivileges',
|
||||
'sdnotify': 'Notify',
|
||||
'pids_limit': 'PidsLimit',
|
||||
'pod': 'Pod',
|
||||
'publish': 'PublishPort',
|
||||
"mount": "Mount",
|
||||
"network": "Network",
|
||||
"network_aliases": "NetworkAlias",
|
||||
"no_new_privileges": "NoNewPrivileges",
|
||||
"sdnotify": "Notify",
|
||||
"pids_limit": "PidsLimit",
|
||||
"pod": "Pod",
|
||||
"publish": "PublishPort",
|
||||
"pull": "Pull",
|
||||
'read_only': 'ReadOnly',
|
||||
'read_only_tmpfs': 'ReadOnlyTmpfs',
|
||||
'rootfs': 'Rootfs',
|
||||
'init': 'RunInit',
|
||||
'SeccompProfile': 'SeccompProfile',
|
||||
'secrets': 'Secret',
|
||||
"read_only": "ReadOnly",
|
||||
"read_only_tmpfs": "ReadOnlyTmpfs",
|
||||
"rootfs": "Rootfs",
|
||||
"init": "RunInit",
|
||||
"SeccompProfile": "SeccompProfile",
|
||||
"secrets": "Secret",
|
||||
# All these are in security_opt
|
||||
'SecurityLabelDisable': 'SecurityLabelDisable',
|
||||
'SecurityLabelFileType': 'SecurityLabelFileType',
|
||||
'SecurityLabelLevel': 'SecurityLabelLevel',
|
||||
'SecurityLabelNested': 'SecurityLabelNested',
|
||||
'SecurityLabelType': 'SecurityLabelType',
|
||||
'shm_size': 'ShmSize',
|
||||
'stop_signal': 'StopSignal',
|
||||
'stop_timeout': 'StopTimeout',
|
||||
'subgidname': 'SubGIDMap',
|
||||
'subuidname': 'SubUIDMap',
|
||||
'sysctl': 'Sysctl',
|
||||
'timezone': 'Timezone',
|
||||
'tmpfs': 'Tmpfs',
|
||||
'uidmap': 'UIDMap',
|
||||
'ulimit': 'Ulimit',
|
||||
'Unmask': 'Unmask', # --security-opt unmask=ALL
|
||||
'user': 'User',
|
||||
'userns': 'UserNS',
|
||||
'volume': 'Volume',
|
||||
'workdir': 'WorkingDir',
|
||||
'podman_args': 'PodmanArgs',
|
||||
"SecurityLabelDisable": "SecurityLabelDisable",
|
||||
"SecurityLabelFileType": "SecurityLabelFileType",
|
||||
"SecurityLabelLevel": "SecurityLabelLevel",
|
||||
"SecurityLabelNested": "SecurityLabelNested",
|
||||
"SecurityLabelType": "SecurityLabelType",
|
||||
"shm_size": "ShmSize",
|
||||
"stop_signal": "StopSignal",
|
||||
"stop_timeout": "StopTimeout",
|
||||
"subgidname": "SubGIDMap",
|
||||
"subuidname": "SubUIDMap",
|
||||
"sysctl": "Sysctl",
|
||||
"timezone": "Timezone",
|
||||
"tmpfs": "Tmpfs",
|
||||
"uidmap": "UIDMap",
|
||||
"ulimit": "Ulimit",
|
||||
"Unmask": "Unmask", # --security-opt unmask=ALL
|
||||
"user": "User",
|
||||
"userns": "UserNS",
|
||||
"volume": "Volume",
|
||||
"workdir": "WorkingDir",
|
||||
"podman_args": "PodmanArgs",
|
||||
}
|
||||
|
||||
def __init__(self, params: dict):
|
||||
|
|
@ -159,27 +167,36 @@ class ContainerQuadlet(Quadlet):
|
|||
"""
|
||||
# Work on params in params_map and convert them to a right form
|
||||
if params["annotation"]:
|
||||
params['annotation'] = ["%s=%s" %
|
||||
(k, v) for k, v in params['annotation'].items()]
|
||||
params["annotation"] = [
|
||||
"%s=%s" % (k, v) for k, v in params["annotation"].items()
|
||||
]
|
||||
if params["cap_add"]:
|
||||
params["cap_add"] = " ".join(params["cap_add"])
|
||||
if params["cap_drop"]:
|
||||
params["cap_drop"] = " ".join(params["cap_drop"])
|
||||
if params["command"]:
|
||||
params["command"] = (" ".join([str(j) for j in params["command"]])
|
||||
if isinstance(params["command"], list)
|
||||
else params["command"])
|
||||
params["command"] = (
|
||||
" ".join([str(j) for j in params["command"]])
|
||||
if isinstance(params["command"], list)
|
||||
else params["command"]
|
||||
)
|
||||
if params["label"]:
|
||||
params["label"] = [shlex.quote("%s=%s" % (k, v)) for k, v in params["label"].items()]
|
||||
params["label"] = [
|
||||
shlex.quote("%s=%s" % (k, v)) for k, v in params["label"].items()
|
||||
]
|
||||
if params["env"]:
|
||||
params["env"] = [shlex.quote("%s=%s" % (k, v)) for k, v in params["env"].items()]
|
||||
params["env"] = [
|
||||
shlex.quote("%s=%s" % (k, v)) for k, v in params["env"].items()
|
||||
]
|
||||
if params["rootfs"]:
|
||||
params["rootfs"] = params["image"]
|
||||
params["image"] = None
|
||||
if params["sysctl"]:
|
||||
params["sysctl"] = ["%s=%s" % (k, v) for k, v in params["sysctl"].items()]
|
||||
if params["tmpfs"]:
|
||||
params["tmpfs"] = ["%s:%s" % (k, v) if v else k for k, v in params["tmpfs"].items()]
|
||||
params["tmpfs"] = [
|
||||
"%s:%s" % (k, v) if v else k for k, v in params["tmpfs"].items()
|
||||
]
|
||||
|
||||
# Work on params which are not in the param_map but can be calculated
|
||||
params["global_args"] = []
|
||||
|
|
@ -208,8 +225,14 @@ class ContainerQuadlet(Quadlet):
|
|||
if params["blkio_weight"]:
|
||||
params["podman_args"].append(f"--blkio-weight {params['blkio_weight']}")
|
||||
if params["blkio_weight_device"]:
|
||||
params["podman_args"].append(" ".join([
|
||||
f"--blkio-weight-device {':'.join(blkio)}" for blkio in params["blkio_weight_device"].items()]))
|
||||
params["podman_args"].append(
|
||||
" ".join(
|
||||
[
|
||||
f"--blkio-weight-device {':'.join(blkio)}"
|
||||
for blkio in params["blkio_weight_device"].items()
|
||||
]
|
||||
)
|
||||
)
|
||||
if params["cgroupns"]:
|
||||
params["podman_args"].append(f"--cgroupns {params['cgroupns']}")
|
||||
if params["cgroup_conf"]:
|
||||
|
|
@ -242,7 +265,9 @@ class ContainerQuadlet(Quadlet):
|
|||
if params["decryption_key"]:
|
||||
params["podman_args"].append(f"--decryption-key {params['decryption_key']}")
|
||||
if params["device_cgroup_rule"]:
|
||||
params["podman_args"].append(f"--device-cgroup-rule {params['device_cgroup_rule']}")
|
||||
params["podman_args"].append(
|
||||
f"--device-cgroup-rule {params['device_cgroup_rule']}"
|
||||
)
|
||||
if params["device_read_bps"]:
|
||||
for i in params["device_read_bps"]:
|
||||
params["podman_args"].append(f"--device-read-bps {i}")
|
||||
|
|
@ -256,7 +281,9 @@ class ContainerQuadlet(Quadlet):
|
|||
for i in params["device_write_iops"]:
|
||||
params["podman_args"].append(f"--device-write-iops {i}")
|
||||
if params["etc_hosts"]:
|
||||
params['etc_hosts'] = ["%s:%s" % (k, v) for k, v in params['etc_hosts'].items()]
|
||||
params["etc_hosts"] = [
|
||||
"%s:%s" % (k, v) for k, v in params["etc_hosts"].items()
|
||||
]
|
||||
if params["env_merge"]:
|
||||
for k, v in params["env_merge"].items():
|
||||
params["podman_args"].append(f"--env {k}={v}")
|
||||
|
|
@ -287,24 +314,32 @@ class ContainerQuadlet(Quadlet):
|
|||
params["podman_args"].append(f"--label-file {params['label_file']}")
|
||||
if params["log_opt"]:
|
||||
params["log_opt"] = [
|
||||
"%s=%s" % (k.replace('max_size', 'max-size'), v)
|
||||
for k, v in params['log_opt'].items() if v is not None]
|
||||
"%s=%s" % (k.replace("max_size", "max-size"), v)
|
||||
for k, v in params["log_opt"].items()
|
||||
if v is not None
|
||||
]
|
||||
if params["mac_address"]:
|
||||
params["podman_args"].append(f"--mac-address {params['mac_address']}")
|
||||
if params["memory"]:
|
||||
params["podman_args"].append(f"--memory {params['memory']}")
|
||||
if params["memory_reservation"]:
|
||||
params["podman_args"].append(f"--memory-reservation {params['memory_reservation']}")
|
||||
params["podman_args"].append(
|
||||
f"--memory-reservation {params['memory_reservation']}"
|
||||
)
|
||||
if params["memory_swap"]:
|
||||
params["podman_args"].append(f"--memory-swap {params['memory_swap']}")
|
||||
if params["memory_swappiness"]:
|
||||
params["podman_args"].append(f"--memory-swappiness {params['memory_swappiness']}")
|
||||
params["podman_args"].append(
|
||||
f"--memory-swappiness {params['memory_swappiness']}"
|
||||
)
|
||||
if params["no_healthcheck"]:
|
||||
params["podman_args"].append("--no-healthcheck")
|
||||
if params["no_hosts"] is not None:
|
||||
params["podman_args"].append(f"--no-hosts={params['no_hosts']}")
|
||||
if params["oom_kill_disable"]:
|
||||
params["podman_args"].append(f"--oom-kill-disable={params['oom_kill_disable']}")
|
||||
params["podman_args"].append(
|
||||
f"--oom-kill-disable={params['oom_kill_disable']}"
|
||||
)
|
||||
if params["oom_score_adj"]:
|
||||
params["podman_args"].append(f"--oom-score-adj {params['oom_score_adj']}")
|
||||
if params["os"]:
|
||||
|
|
@ -350,7 +385,9 @@ class ContainerQuadlet(Quadlet):
|
|||
for security_opt in params["security_opt"]:
|
||||
params["podman_args"].append(f"--security-opt {security_opt}")
|
||||
if params["shm_size_systemd"]:
|
||||
params["podman_args"].append(f"--shm-size-systemd {params['shm_size_systemd']}")
|
||||
params["podman_args"].append(
|
||||
f"--shm-size-systemd {params['shm_size_systemd']}"
|
||||
)
|
||||
if params["sig_proxy"]:
|
||||
params["podman_args"].append(f"--sig-proxy {params['sig_proxy']}")
|
||||
if params["systemd"]:
|
||||
|
|
@ -358,7 +395,9 @@ class ContainerQuadlet(Quadlet):
|
|||
if params["timeout"]:
|
||||
params["podman_args"].append(f"--timeout {params['timeout']}")
|
||||
if params["tls_verify"]:
|
||||
params["podman_args"].append(f"--tls-verify={str(params['tls_verify']).lower()}")
|
||||
params["podman_args"].append(
|
||||
f"--tls-verify={str(params['tls_verify']).lower()}"
|
||||
)
|
||||
if params["tty"]:
|
||||
params["podman_args"].append("--tty")
|
||||
if params["umask"]:
|
||||
|
|
@ -384,17 +423,17 @@ class ContainerQuadlet(Quadlet):
|
|||
|
||||
class NetworkQuadlet(Quadlet):
|
||||
param_map = {
|
||||
'name': 'NetworkName',
|
||||
'internal': 'Internal',
|
||||
'driver': 'Driver',
|
||||
'gateway': 'Gateway',
|
||||
'disable_dns': 'DisableDNS',
|
||||
'subnet': 'Subnet',
|
||||
'ip_range': 'IPRange',
|
||||
'ipv6': 'IPv6',
|
||||
"name": "NetworkName",
|
||||
"internal": "Internal",
|
||||
"driver": "Driver",
|
||||
"gateway": "Gateway",
|
||||
"disable_dns": "DisableDNS",
|
||||
"subnet": "Subnet",
|
||||
"ip_range": "IPRange",
|
||||
"ipv6": "IPv6",
|
||||
"opt": "Options",
|
||||
# Add more parameter mappings specific to networks
|
||||
'ContainersConfModule': 'ContainersConfModule',
|
||||
"ContainersConfModule": "ContainersConfModule",
|
||||
"dns": "DNS",
|
||||
"ipam_driver": "IPAMDriver",
|
||||
"Label": "Label",
|
||||
|
|
@ -424,11 +463,11 @@ class NetworkQuadlet(Quadlet):
|
|||
# This is a inherited class that represents a Quadlet file for the Podman pod
|
||||
class PodQuadlet(Quadlet):
|
||||
param_map = {
|
||||
'name': 'PodName',
|
||||
"name": "PodName",
|
||||
"network": "Network",
|
||||
"publish": "PublishPort",
|
||||
"volume": "Volume",
|
||||
'ContainersConfModule': 'ContainersConfModule',
|
||||
"ContainersConfModule": "ContainersConfModule",
|
||||
"global_args": "GlobalArgs",
|
||||
"podman_args": "PodmanArgs",
|
||||
}
|
||||
|
|
@ -445,15 +484,21 @@ class PodQuadlet(Quadlet):
|
|||
params["podman_args"] = []
|
||||
|
||||
if params["add_host"]:
|
||||
for host in params['add_host']:
|
||||
for host in params["add_host"]:
|
||||
params["podman_args"].append(f"--add-host {host}")
|
||||
if params["cgroup_parent"]:
|
||||
params["podman_args"].append(f"--cgroup-parent {params['cgroup_parent']}")
|
||||
if params["blkio_weight"]:
|
||||
params["podman_args"].append(f"--blkio-weight {params['blkio_weight']}")
|
||||
if params["blkio_weight_device"]:
|
||||
params["podman_args"].append(" ".join([
|
||||
f"--blkio-weight-device {':'.join(blkio)}" for blkio in params["blkio_weight_device"].items()]))
|
||||
params["podman_args"].append(
|
||||
" ".join(
|
||||
[
|
||||
f"--blkio-weight-device {':'.join(blkio)}"
|
||||
for blkio in params["blkio_weight_device"].items()
|
||||
]
|
||||
)
|
||||
)
|
||||
if params["cpuset_cpus"]:
|
||||
params["podman_args"].append(f"--cpuset-cpus {params['cpuset_cpus']}")
|
||||
if params["cpuset_mems"]:
|
||||
|
|
@ -494,7 +539,9 @@ class PodQuadlet(Quadlet):
|
|||
if params["infra_command"]:
|
||||
params["podman_args"].append(f"--infra-command {params['infra_command']}")
|
||||
if params["infra_conmon_pidfile"]:
|
||||
params["podman_args"].append(f"--infra-conmon-pidfile {params['infra_conmon_pidfile']}")
|
||||
params["podman_args"].append(
|
||||
f"--infra-conmon-pidfile {params['infra_conmon_pidfile']}"
|
||||
)
|
||||
if params["infra_image"]:
|
||||
params["podman_args"].append(f"--infra-image {params['infra_image']}")
|
||||
if params["infra_name"]:
|
||||
|
|
@ -528,11 +575,15 @@ class PodQuadlet(Quadlet):
|
|||
if params["share"]:
|
||||
params["podman_args"].append(f"--share {params['share']}")
|
||||
if params["share_parent"] is not None:
|
||||
params["podman_args"].append(f"--share-parent={str(params['share_parent']).lower()}")
|
||||
params["podman_args"].append(
|
||||
f"--share-parent={str(params['share_parent']).lower()}"
|
||||
)
|
||||
if params["shm_size"]:
|
||||
params["podman_args"].append(f"--shm-size {params['shm_size']}")
|
||||
if params["shm_size_systemd"]:
|
||||
params["podman_args"].append(f"--shm-size-systemd {params['shm_size_systemd']}")
|
||||
params["podman_args"].append(
|
||||
f"--shm-size-systemd {params['shm_size_systemd']}"
|
||||
)
|
||||
if params["subgidname"]:
|
||||
params["podman_args"].append(f"--subgidname {params['subgidname']}")
|
||||
if params["subuidname"]:
|
||||
|
|
@ -559,13 +610,13 @@ class PodQuadlet(Quadlet):
|
|||
# This is a inherited class that represents a Quadlet file for the Podman volume
|
||||
class VolumeQuadlet(Quadlet):
|
||||
param_map = {
|
||||
'name': 'VolumeName',
|
||||
'driver': 'Driver',
|
||||
'label': 'Label',
|
||||
"name": "VolumeName",
|
||||
"driver": "Driver",
|
||||
"label": "Label",
|
||||
# 'opt': 'Options',
|
||||
'ContainersConfModule': 'ContainersConfModule',
|
||||
'global_args': 'GlobalArgs',
|
||||
'podman_args': 'PodmanArgs',
|
||||
"ContainersConfModule": "ContainersConfModule",
|
||||
"global_args": "GlobalArgs",
|
||||
"podman_args": "PodmanArgs",
|
||||
}
|
||||
|
||||
def __init__(self, params: dict):
|
||||
|
|
@ -593,19 +644,19 @@ class VolumeQuadlet(Quadlet):
|
|||
# This is a inherited class that represents a Quadlet file for the Podman kube
|
||||
class KubeQuadlet(Quadlet):
|
||||
param_map = {
|
||||
'configmap': 'ConfigMap',
|
||||
'log_driver': 'LogDriver',
|
||||
'network': 'Network',
|
||||
'kube_file': 'Yaml',
|
||||
'userns': 'UserNS',
|
||||
'AutoUpdate': 'AutoUpdate',
|
||||
'ExitCodePropagation': 'ExitCodePropagation',
|
||||
'KubeDownForce': 'KubeDownForce',
|
||||
'PublishPort': 'PublishPort',
|
||||
'SetWorkingDirectory': 'SetWorkingDirectory',
|
||||
'ContainersConfModule': 'ContainersConfModule',
|
||||
'global_args': 'GlobalArgs',
|
||||
'podman_args': 'PodmanArgs',
|
||||
"configmap": "ConfigMap",
|
||||
"log_driver": "LogDriver",
|
||||
"network": "Network",
|
||||
"kube_file": "Yaml",
|
||||
"userns": "UserNS",
|
||||
"AutoUpdate": "AutoUpdate",
|
||||
"ExitCodePropagation": "ExitCodePropagation",
|
||||
"KubeDownForce": "KubeDownForce",
|
||||
"PublishPort": "PublishPort",
|
||||
"SetWorkingDirectory": "SetWorkingDirectory",
|
||||
"ContainersConfModule": "ContainersConfModule",
|
||||
"global_args": "GlobalArgs",
|
||||
"podman_args": "PodmanArgs",
|
||||
}
|
||||
|
||||
def __init__(self, params: dict):
|
||||
|
|
@ -628,20 +679,20 @@ class KubeQuadlet(Quadlet):
|
|||
# This is a inherited class that represents a Quadlet file for the Podman image
|
||||
class ImageQuadlet(Quadlet):
|
||||
param_map = {
|
||||
'AllTags': 'AllTags',
|
||||
'arch': 'Arch',
|
||||
'authfile': 'AuthFile',
|
||||
'ca_cert_dir': 'CertDir',
|
||||
'creds': 'Creds',
|
||||
'DecryptionKey': 'DecryptionKey',
|
||||
'name': 'Image',
|
||||
'ImageTag': 'ImageTag',
|
||||
'OS': 'OS',
|
||||
'validate_certs': 'TLSVerify',
|
||||
'Variant': 'Variant',
|
||||
'ContainersConfModule': 'ContainersConfModule',
|
||||
'global_args': 'GlobalArgs',
|
||||
'podman_args': 'PodmanArgs',
|
||||
"AllTags": "AllTags",
|
||||
"arch": "Arch",
|
||||
"authfile": "AuthFile",
|
||||
"ca_cert_dir": "CertDir",
|
||||
"creds": "Creds",
|
||||
"DecryptionKey": "DecryptionKey",
|
||||
"name": "Image",
|
||||
"ImageTag": "ImageTag",
|
||||
"OS": "OS",
|
||||
"validate_certs": "TLSVerify",
|
||||
"Variant": "Variant",
|
||||
"ContainersConfModule": "ContainersConfModule",
|
||||
"global_args": "GlobalArgs",
|
||||
"podman_args": "PodmanArgs",
|
||||
}
|
||||
|
||||
def __init__(self, params: dict):
|
||||
|
|
@ -664,20 +715,20 @@ class ImageQuadlet(Quadlet):
|
|||
|
||||
|
||||
def check_quadlet_directory(module, quadlet_dir):
|
||||
'''Check if the directory exists and is writable. If not, fail the module.'''
|
||||
"""Check if the directory exists and is writable. If not, fail the module."""
|
||||
if not os.path.exists(quadlet_dir):
|
||||
try:
|
||||
os.makedirs(quadlet_dir)
|
||||
except Exception as e:
|
||||
module.fail_json(
|
||||
msg="Directory for quadlet_file can't be created: %s" % e)
|
||||
module.fail_json(msg="Directory for quadlet_file can't be created: %s" % e)
|
||||
if not os.access(quadlet_dir, os.W_OK):
|
||||
module.fail_json(
|
||||
msg="Directory for quadlet_file is not writable: %s" % quadlet_dir)
|
||||
msg="Directory for quadlet_file is not writable: %s" % quadlet_dir
|
||||
)
|
||||
|
||||
|
||||
def create_quadlet_state(module, issuer):
|
||||
'''Create a quadlet file for the specified issuer.'''
|
||||
"""Create a quadlet file for the specified issuer."""
|
||||
class_map = {
|
||||
"container": ContainerQuadlet,
|
||||
"network": NetworkQuadlet,
|
||||
|
|
@ -688,20 +739,22 @@ def create_quadlet_state(module, issuer):
|
|||
}
|
||||
# Let's detect which user is running
|
||||
user = "root" if os.geteuid() == 0 else "user"
|
||||
quadlet_dir = module.params.get('quadlet_dir')
|
||||
quadlet_dir = module.params.get("quadlet_dir")
|
||||
if not quadlet_dir:
|
||||
if user == "root":
|
||||
quadlet_dir = QUADLET_ROOT_PATH
|
||||
else:
|
||||
quadlet_dir = os.path.expanduser(QUADLET_NON_ROOT_PATH)
|
||||
# Create a filename based on the issuer
|
||||
if not module.params.get('name') and not module.params.get('quadlet_filename'):
|
||||
module.fail_json(msg=f"Filename for {issuer} is required for creating a quadlet file.")
|
||||
if not module.params.get("name") and not module.params.get("quadlet_filename"):
|
||||
module.fail_json(
|
||||
msg=f"Filename for {issuer} is required for creating a quadlet file."
|
||||
)
|
||||
if issuer == "image":
|
||||
name = module.params['name'].split("/")[-1].split(":")[0]
|
||||
name = module.params["name"].split("/")[-1].split(":")[0]
|
||||
else:
|
||||
name = module.params.get('name')
|
||||
quad_file_name = module.params['quadlet_filename']
|
||||
name = module.params.get("name")
|
||||
quad_file_name = module.params["quadlet_filename"]
|
||||
if quad_file_name and not quad_file_name.endswith(f".{issuer}"):
|
||||
quad_file_name = f"{quad_file_name}.{issuer}"
|
||||
filename = quad_file_name or f"{name}.{issuer}"
|
||||
|
|
@ -710,10 +763,10 @@ def create_quadlet_state(module, issuer):
|
|||
if not module.check_mode:
|
||||
check_quadlet_directory(module, quadlet_dir)
|
||||
# Specify file permissions
|
||||
mode = module.params.get('quadlet_file_mode', None)
|
||||
mode = module.params.get("quadlet_file_mode", None)
|
||||
if mode is None and not os.path.exists(quadlet_file_path):
|
||||
# default mode for new quadlet file only
|
||||
mode = '0640'
|
||||
mode = "0640"
|
||||
# Check if file already exists and if it's different
|
||||
quadlet = class_map[issuer](module.params)
|
||||
quadlet_content = quadlet.create_quadlet_content()
|
||||
|
|
@ -724,22 +777,31 @@ def create_quadlet_state(module, issuer):
|
|||
if mode is not None:
|
||||
module.set_mode_if_different(quadlet_file_path, mode, False)
|
||||
results_update = {
|
||||
'changed': True,
|
||||
"changed": True,
|
||||
"diff": {
|
||||
"before": "\n".join(file_diff[0]) if isinstance(file_diff[0], list) else file_diff[0] + "\n",
|
||||
"after": "\n".join(file_diff[1]) if isinstance(file_diff[1], list) else file_diff[1] + "\n",
|
||||
}}
|
||||
"before": (
|
||||
"\n".join(file_diff[0])
|
||||
if isinstance(file_diff[0], list)
|
||||
else file_diff[0] + "\n"
|
||||
),
|
||||
"after": (
|
||||
"\n".join(file_diff[1])
|
||||
if isinstance(file_diff[1], list)
|
||||
else file_diff[1] + "\n"
|
||||
),
|
||||
},
|
||||
}
|
||||
else:
|
||||
# adjust file permissions
|
||||
diff = {}
|
||||
if mode is not None and module.set_mode_if_different(quadlet_file_path, mode, False, diff):
|
||||
results_update = {
|
||||
'changed': True,
|
||||
'diff': diff
|
||||
}
|
||||
if mode is not None and module.set_mode_if_different(
|
||||
quadlet_file_path, mode, False, diff
|
||||
):
|
||||
results_update = {"changed": True, "diff": diff}
|
||||
else:
|
||||
results_update = {}
|
||||
return results_update
|
||||
|
||||
|
||||
# Check with following command:
|
||||
# QUADLET_UNIT_DIRS=<Directory> /usr/lib/systemd/system-generators/podman-system-generator {--user} --dryrun
|
||||
|
|
|
|||
|
|
@ -1522,28 +1522,31 @@ container:
|
|||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: F402
|
||||
from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402
|
||||
from ..module_utils.podman.podman_container_lib import ARGUMENTS_SPEC_CONTAINER # noqa: F402
|
||||
from ..module_utils.podman.podman_container_lib import (
|
||||
ARGUMENTS_SPEC_CONTAINER,
|
||||
) # noqa: F402
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=ARGUMENTS_SPEC_CONTAINER,
|
||||
mutually_exclusive=(
|
||||
['no_hosts', 'etc_hosts'],
|
||||
),
|
||||
mutually_exclusive=(["no_hosts", "etc_hosts"],),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
# work on input vars
|
||||
if (module.params['state'] in ['present', 'created']
|
||||
and not module.params['force_restart']
|
||||
and not module.params['image']):
|
||||
module.fail_json(msg="State '%s' required image to be configured!" %
|
||||
module.params['state'])
|
||||
if (
|
||||
module.params["state"] in ["present", "created"]
|
||||
and not module.params["force_restart"]
|
||||
and not module.params["image"]
|
||||
):
|
||||
module.fail_json(
|
||||
msg="State '%s' required image to be configured!" % module.params["state"]
|
||||
)
|
||||
|
||||
results = PodmanManager(module, module.params).execute()
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_container_copy
|
||||
author:
|
||||
- Alessandro Rossi (@kubealex)
|
||||
|
|
@ -54,7 +55,7 @@ options:
|
|||
required: False
|
||||
default: False
|
||||
type: bool
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Copy file "test.yml" on the host to the "apache" container's root folder
|
||||
|
|
@ -73,22 +74,26 @@ EXAMPLES = r"""
|
|||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def copy_file(module, executable, src, dest, container, from_container, archive, overwrite):
|
||||
def copy_file(
|
||||
module, executable, src, dest, container, from_container, archive, overwrite
|
||||
):
|
||||
if from_container:
|
||||
command = [executable, 'cp', '{0}:{1}'.format(container, src), dest]
|
||||
command = [executable, "cp", "{0}:{1}".format(container, src), dest]
|
||||
else:
|
||||
command = [executable, 'cp', src, '{0}:{1}'.format(container, dest)]
|
||||
command = [executable, "cp", src, "{0}:{1}".format(container, dest)]
|
||||
|
||||
if not archive:
|
||||
command.append('--archive=False')
|
||||
command.append("--archive=False")
|
||||
|
||||
if overwrite:
|
||||
command.append('--overwrite')
|
||||
command.append("--overwrite")
|
||||
|
||||
rc, out, err = module.run_command(command)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(msg='Unable to copy file to/from container - {out}'.format(out=err))
|
||||
module.fail_json(
|
||||
msg="Unable to copy file to/from container - {out}".format(out=err)
|
||||
)
|
||||
else:
|
||||
changed = True
|
||||
return changed, out, err
|
||||
|
|
@ -97,35 +102,35 @@ def copy_file(module, executable, src, dest, container, from_container, archive,
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
src=dict(type='str', required=True),
|
||||
dest=dict(type='str', required=True),
|
||||
container=dict(type='str', required=True),
|
||||
from_container=dict(type='bool', required=False, default=False),
|
||||
archive=dict(type='bool', required=False, default=True),
|
||||
overwrite=dict(type='bool', required=False, default=False)
|
||||
executable=dict(type="str", default="podman"),
|
||||
src=dict(type="str", required=True),
|
||||
dest=dict(type="str", required=True),
|
||||
container=dict(type="str", required=True),
|
||||
from_container=dict(type="bool", required=False, default=False),
|
||||
archive=dict(type="bool", required=False, default=True),
|
||||
overwrite=dict(type="bool", required=False, default=False),
|
||||
),
|
||||
supports_check_mode=False,
|
||||
)
|
||||
|
||||
executable = module.params['executable']
|
||||
src = module.params['src']
|
||||
dest = module.params['dest']
|
||||
container = module.params['container']
|
||||
from_container = module.params['from_container']
|
||||
archive = module.params['archive']
|
||||
overwrite = module.params['overwrite']
|
||||
executable = module.params["executable"]
|
||||
src = module.params["src"]
|
||||
dest = module.params["dest"]
|
||||
container = module.params["container"]
|
||||
from_container = module.params["from_container"]
|
||||
archive = module.params["archive"]
|
||||
overwrite = module.params["overwrite"]
|
||||
|
||||
executable = module.get_bin_path(executable, required=True)
|
||||
|
||||
changed, out, err = copy_file(module, executable, src, dest, container, from_container, archive, overwrite)
|
||||
|
||||
results = dict(
|
||||
changed=changed
|
||||
changed, out, err = copy_file(
|
||||
module, executable, src, dest, container, from_container, archive, overwrite
|
||||
)
|
||||
|
||||
results = dict(changed=changed)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
# Copyright (c) 2023, Takuya Nishimura <@nishipy>
|
||||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_container_exec
|
||||
author:
|
||||
- Takuya Nishimura (@nishipy)
|
||||
|
|
@ -68,9 +69,9 @@ requirements:
|
|||
- podman
|
||||
notes:
|
||||
- See L(the Podman documentation,https://docs.podman.io/en/latest/markdown/podman-exec.1.html) for details of podman-exec(1).
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r'''
|
||||
EXAMPLES = r"""
|
||||
- name: Execute a command with workdir
|
||||
containers.podman.podman_container_exec:
|
||||
name: ubi8
|
||||
|
|
@ -93,9 +94,9 @@ EXAMPLES = r'''
|
|||
name: detach_container
|
||||
command: "cat redhat-release"
|
||||
detach: true
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = r'''
|
||||
RETURN = r"""
|
||||
stdout:
|
||||
type: str
|
||||
returned: success
|
||||
|
|
@ -118,65 +119,65 @@ exec_id:
|
|||
sample: f99002e34c1087fd1aa08d5027e455bf7c2d6b74f019069acf6462a96ddf2a47
|
||||
description:
|
||||
- The ID of the exec session.
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
import shlex
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import run_podman_command
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
run_podman_command,
|
||||
)
|
||||
|
||||
|
||||
def run_container_exec(module: AnsibleModule) -> dict:
|
||||
'''
|
||||
"""
|
||||
Execute podman-container-exec for the given options
|
||||
'''
|
||||
exec_with_args = ['container', 'exec']
|
||||
"""
|
||||
exec_with_args = ["container", "exec"]
|
||||
# podman_container_exec always returns changed=true
|
||||
changed = True
|
||||
exec_options = []
|
||||
|
||||
name = module.params['name']
|
||||
argv = module.params['argv']
|
||||
command = module.params['command']
|
||||
detach = module.params['detach']
|
||||
env = module.params['env']
|
||||
privileged = module.params['privileged']
|
||||
tty = module.params['tty']
|
||||
user = module.params['user']
|
||||
workdir = module.params['workdir']
|
||||
executable = module.params['executable']
|
||||
name = module.params["name"]
|
||||
argv = module.params["argv"]
|
||||
command = module.params["command"]
|
||||
detach = module.params["detach"]
|
||||
env = module.params["env"]
|
||||
privileged = module.params["privileged"]
|
||||
tty = module.params["tty"]
|
||||
user = module.params["user"]
|
||||
workdir = module.params["workdir"]
|
||||
executable = module.params["executable"]
|
||||
|
||||
if command is not None:
|
||||
argv = shlex.split(command)
|
||||
|
||||
if detach:
|
||||
exec_options.append('--detach')
|
||||
exec_options.append("--detach")
|
||||
|
||||
if env is not None:
|
||||
for key, value in env.items():
|
||||
if not isinstance(value, string_types):
|
||||
module.fail_json(
|
||||
msg="Specify string value %s on the env field" % (value))
|
||||
msg="Specify string value %s on the env field" % (value)
|
||||
)
|
||||
|
||||
to_text(value, errors='surrogate_or_strict')
|
||||
exec_options += ['--env',
|
||||
'%s=%s' % (key, value)]
|
||||
to_text(value, errors="surrogate_or_strict")
|
||||
exec_options += ["--env", "%s=%s" % (key, value)]
|
||||
|
||||
if privileged:
|
||||
exec_options.append('--privileged')
|
||||
exec_options.append("--privileged")
|
||||
|
||||
if tty:
|
||||
exec_options.append('--tty')
|
||||
exec_options.append("--tty")
|
||||
|
||||
if user is not None:
|
||||
exec_options += ['--user',
|
||||
to_text(user, errors='surrogate_or_strict')]
|
||||
exec_options += ["--user", to_text(user, errors="surrogate_or_strict")]
|
||||
|
||||
if workdir is not None:
|
||||
exec_options += ['--workdir',
|
||||
to_text(workdir, errors='surrogate_or_strict')]
|
||||
exec_options += ["--workdir", to_text(workdir, errors="surrogate_or_strict")]
|
||||
|
||||
exec_options.append(name)
|
||||
exec_options.extend(argv)
|
||||
|
|
@ -184,71 +185,72 @@ def run_container_exec(module: AnsibleModule) -> dict:
|
|||
exec_with_args.extend(exec_options)
|
||||
|
||||
rc, stdout, stderr = run_podman_command(
|
||||
module=module, executable=executable, args=exec_with_args, ignore_errors=True)
|
||||
module=module, executable=executable, args=exec_with_args, ignore_errors=True
|
||||
)
|
||||
|
||||
result = {
|
||||
'changed': changed,
|
||||
'podman_command': exec_options,
|
||||
'rc': rc,
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
"changed": changed,
|
||||
"podman_command": exec_options,
|
||||
"rc": rc,
|
||||
"stdout": stdout,
|
||||
"stderr": stderr,
|
||||
}
|
||||
|
||||
if detach:
|
||||
result['exec_id'] = stdout.replace('\n', '')
|
||||
result["exec_id"] = stdout.replace("\n", "")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = {
|
||||
'name': {
|
||||
'type': 'str',
|
||||
'required': True,
|
||||
"name": {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
},
|
||||
'command': {
|
||||
'type': 'str',
|
||||
"command": {
|
||||
"type": "str",
|
||||
},
|
||||
'argv': {
|
||||
'type': 'list',
|
||||
'elements': 'str',
|
||||
"argv": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
},
|
||||
'detach': {
|
||||
'type': 'bool',
|
||||
'default': False,
|
||||
"detach": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
},
|
||||
'executable': {
|
||||
'type': 'str',
|
||||
'default': 'podman',
|
||||
"executable": {
|
||||
"type": "str",
|
||||
"default": "podman",
|
||||
},
|
||||
'env': {
|
||||
'type': 'dict',
|
||||
"env": {
|
||||
"type": "dict",
|
||||
},
|
||||
'privileged': {
|
||||
'type': 'bool',
|
||||
'default': False,
|
||||
"privileged": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
},
|
||||
'tty': {
|
||||
'type': 'bool',
|
||||
'default': False,
|
||||
"tty": {
|
||||
"type": "bool",
|
||||
"default": False,
|
||||
},
|
||||
'user': {
|
||||
'type': 'str',
|
||||
"user": {
|
||||
"type": "str",
|
||||
},
|
||||
'workdir': {
|
||||
'type': 'str',
|
||||
"workdir": {
|
||||
"type": "str",
|
||||
},
|
||||
}
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=[('argv', 'command')],
|
||||
required_one_of=[("argv", "command")],
|
||||
)
|
||||
|
||||
result = run_container_exec(module)
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_container_info
|
||||
author:
|
||||
- Sagi Shnaidman (@podman)
|
||||
|
|
@ -31,7 +32,7 @@ options:
|
|||
machine running C(podman)
|
||||
default: 'podman'
|
||||
type: str
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Gather facts for all containers
|
||||
|
|
@ -338,7 +339,7 @@ def get_containers_facts(module, executable, name):
|
|||
retry = 0
|
||||
retry_limit = 4
|
||||
if not name:
|
||||
all_names = [executable, 'container', 'ls', '-q', '-a']
|
||||
all_names = [executable, "container", "ls", "-q", "-a"]
|
||||
rc, out, err = module.run_command(all_names)
|
||||
# This should not fail in regular circumstances, so retry again
|
||||
# https://github.com/containers/podman/issues/10225
|
||||
|
|
@ -348,12 +349,14 @@ def get_containers_facts(module, executable, name):
|
|||
retry += 1
|
||||
rc, out, err = module.run_command(all_names)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Unable to get list of containers during"
|
||||
" %s retries" % retry_limit)
|
||||
module.fail_json(
|
||||
msg="Unable to get list of containers during"
|
||||
" %s retries" % retry_limit
|
||||
)
|
||||
name = out.split()
|
||||
if not name:
|
||||
return [], out, err
|
||||
command = [executable, 'container', 'inspect']
|
||||
command = [executable, "container", "inspect"]
|
||||
command.extend(name)
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc == 0:
|
||||
|
|
@ -361,7 +364,7 @@ def get_containers_facts(module, executable, name):
|
|||
if json_out is None:
|
||||
return [], out, err
|
||||
return json_out, out, err
|
||||
if rc != 0 and 'no such ' in err:
|
||||
if rc != 0 and "no such " in err:
|
||||
if len(name) < 2:
|
||||
return [], out, err
|
||||
return cycle_over(module, executable, name)
|
||||
|
|
@ -382,9 +385,9 @@ def cycle_over(module, executable, name):
|
|||
inspection = []
|
||||
stderrs = []
|
||||
for container in name:
|
||||
command = [executable, 'container', 'inspect', container]
|
||||
command = [executable, "container", "inspect", container]
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0 and 'no such ' not in err:
|
||||
if rc != 0 and "no such " not in err:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (container, err))
|
||||
if rc == 0 and out:
|
||||
json_out = json.loads(out)
|
||||
|
|
@ -397,25 +400,21 @@ def cycle_over(module, executable, name):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec={
|
||||
'executable': {'type': 'str', 'default': 'podman'},
|
||||
'name': {'type': 'list', 'elements': 'str'},
|
||||
"executable": {"type": "str", "default": "podman"},
|
||||
"name": {"type": "list", "elements": "str"},
|
||||
},
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
name = module.params["name"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
# pylint: disable=unused-variable
|
||||
inspect_results, out, err = get_containers_facts(module, executable, name)
|
||||
|
||||
results = {
|
||||
"changed": False,
|
||||
"containers": inspect_results,
|
||||
"stderr": err
|
||||
}
|
||||
results = {"changed": False, "containers": inspect_results, "stderr": err}
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: podman_containers
|
||||
author:
|
||||
|
|
@ -29,9 +29,9 @@ options:
|
|||
- Return additional information which can be helpful for investigations.
|
||||
type: bool
|
||||
default: False
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
- name: Run three containers at once
|
||||
podman_containers:
|
||||
containers:
|
||||
|
|
@ -43,7 +43,7 @@ EXAMPLES = '''
|
|||
- name: test
|
||||
image: python:3.10-alpine
|
||||
command: python -V
|
||||
'''
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: F402
|
||||
from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402
|
||||
|
|
@ -51,82 +51,81 @@ from ..module_utils.podman.podman_container_lib import set_container_opts # noq
|
|||
|
||||
|
||||
def combine(results):
|
||||
changed = any(i.get('changed', False) for i in results)
|
||||
failed = any(i.get('failed', False) for i in results)
|
||||
changed = any(i.get("changed", False) for i in results)
|
||||
failed = any(i.get("failed", False) for i in results)
|
||||
actions = []
|
||||
podman_actions = []
|
||||
containers = []
|
||||
podman_version = ''
|
||||
podman_version = ""
|
||||
diffs = {}
|
||||
stderr = ''
|
||||
stdout = ''
|
||||
stderr = ""
|
||||
stdout = ""
|
||||
for i in results:
|
||||
if 'actions' in i and i['actions']:
|
||||
actions += i['actions']
|
||||
if 'podman_actions' in i and i['podman_actions']:
|
||||
podman_actions += i['podman_actions']
|
||||
if 'container' in i and i['container']:
|
||||
containers.append(i['container'])
|
||||
if 'podman_version' in i:
|
||||
podman_version = i['podman_version']
|
||||
if 'diff' in i:
|
||||
diffs[i['container']['Name']] = i['diff']
|
||||
if 'stderr' in i:
|
||||
stderr += i['stderr']
|
||||
if 'stdout' in i:
|
||||
stdout += i['stdout']
|
||||
if "actions" in i and i["actions"]:
|
||||
actions += i["actions"]
|
||||
if "podman_actions" in i and i["podman_actions"]:
|
||||
podman_actions += i["podman_actions"]
|
||||
if "container" in i and i["container"]:
|
||||
containers.append(i["container"])
|
||||
if "podman_version" in i:
|
||||
podman_version = i["podman_version"]
|
||||
if "diff" in i:
|
||||
diffs[i["container"]["Name"]] = i["diff"]
|
||||
if "stderr" in i:
|
||||
stderr += i["stderr"]
|
||||
if "stdout" in i:
|
||||
stdout += i["stdout"]
|
||||
|
||||
total = {
|
||||
'changed': changed,
|
||||
'failed': failed,
|
||||
'actions': actions,
|
||||
'podman_actions': podman_actions,
|
||||
'containers': containers,
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
"changed": changed,
|
||||
"failed": failed,
|
||||
"actions": actions,
|
||||
"podman_actions": podman_actions,
|
||||
"containers": containers,
|
||||
"stdout": stdout,
|
||||
"stderr": stderr,
|
||||
}
|
||||
if podman_version:
|
||||
total['podman_version'] = podman_version
|
||||
total["podman_version"] = podman_version
|
||||
if diffs:
|
||||
before = after = ''
|
||||
before = after = ""
|
||||
for k, v in diffs.items():
|
||||
before += "".join([str(k), ": ", str(v['before']), "\n"])
|
||||
after += "".join([str(k), ": ", str(v['after']), "\n"])
|
||||
total['diff'] = {
|
||||
'before': before,
|
||||
'after': after
|
||||
}
|
||||
before += "".join([str(k), ": ", str(v["before"]), "\n"])
|
||||
after += "".join([str(k), ": ", str(v["after"]), "\n"])
|
||||
total["diff"] = {"before": before, "after": after}
|
||||
return total
|
||||
|
||||
|
||||
def check_input_strict(container):
|
||||
if container['state'] in ['started', 'present'] and not container['image']:
|
||||
return "State '%s' required image to be configured!" % container['state']
|
||||
if container["state"] in ["started", "present"] and not container["image"]:
|
||||
return "State '%s' required image to be configured!" % container["state"]
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
containers=dict(type='list', elements='dict', required=True),
|
||||
debug=dict(type='bool', default=False),
|
||||
containers=dict(type="list", elements="dict", required=True),
|
||||
debug=dict(type="bool", default=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
# work on input vars
|
||||
|
||||
results = []
|
||||
for container in module.params['containers']:
|
||||
for container in module.params["containers"]:
|
||||
options_dict = set_container_opts(container)
|
||||
options_dict['debug'] = module.params['debug'] or options_dict['debug']
|
||||
options_dict["debug"] = module.params["debug"] or options_dict["debug"]
|
||||
test_input = check_input_strict(options_dict)
|
||||
if test_input:
|
||||
module.fail_json(
|
||||
msg="Failed to run container %s because: %s" % (options_dict['name'], test_input))
|
||||
msg="Failed to run container %s because: %s"
|
||||
% (options_dict["name"], test_input)
|
||||
)
|
||||
res = PodmanManager(module, options_dict).execute()
|
||||
results.append(res)
|
||||
total_results = combine(results)
|
||||
module.exit_json(**total_results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_export
|
||||
short_description: Export a podman container
|
||||
author: Sagi Shnaidman (@sshnaidm)
|
||||
|
|
@ -41,12 +41,12 @@ options:
|
|||
type: str
|
||||
requirements:
|
||||
- "Podman installed on host"
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
# What modules does for example
|
||||
- containers.podman.podman_export:
|
||||
dest: /path/to/tar/file
|
||||
|
|
@ -54,7 +54,7 @@ EXAMPLES = '''
|
|||
- containers.podman.podman_export:
|
||||
dest: /path/to/tar/file
|
||||
volume: volume-name
|
||||
'''
|
||||
"""
|
||||
|
||||
import os # noqa: E402
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
||||
|
|
@ -63,56 +63,58 @@ from ..module_utils.podman.common import remove_file_or_dir # noqa: E402
|
|||
|
||||
def export(module, executable):
|
||||
changed = False
|
||||
export_type = ''
|
||||
export_type = ""
|
||||
command = []
|
||||
if module.params['container']:
|
||||
export_type = 'container'
|
||||
command = [executable, 'export']
|
||||
if module.params["container"]:
|
||||
export_type = "container"
|
||||
command = [executable, "export"]
|
||||
else:
|
||||
export_type = 'volume'
|
||||
command = [executable, 'volume', 'export']
|
||||
export_type = "volume"
|
||||
command = [executable, "volume", "export"]
|
||||
|
||||
command += ['-o=%s' % module.params['dest'], module.params[export_type]]
|
||||
if module.params['force']:
|
||||
dest = module.params['dest']
|
||||
command += ["-o=%s" % module.params["dest"], module.params[export_type]]
|
||||
if module.params["force"]:
|
||||
dest = module.params["dest"]
|
||||
if os.path.exists(dest):
|
||||
changed = True
|
||||
if module.check_mode:
|
||||
return changed, '', ''
|
||||
return changed, "", ""
|
||||
try:
|
||||
remove_file_or_dir(dest)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error deleting %s path: %s" % (dest, e))
|
||||
else:
|
||||
changed = not os.path.exists(module.params['dest'])
|
||||
changed = not os.path.exists(module.params["dest"])
|
||||
if module.check_mode:
|
||||
return changed, '', ''
|
||||
return changed, "", ""
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Error exporting %s %s: %s" % (export_type,
|
||||
module.params['container'], err))
|
||||
module.fail_json(
|
||||
msg="Error exporting %s %s: %s"
|
||||
% (export_type, module.params["container"], err)
|
||||
)
|
||||
return changed, out, err
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
dest=dict(type='str', required=True),
|
||||
container=dict(type='str'),
|
||||
volume=dict(type='str'),
|
||||
force=dict(type='bool', default=True),
|
||||
executable=dict(type='str', default='podman')
|
||||
dest=dict(type="str", required=True),
|
||||
container=dict(type="str"),
|
||||
volume=dict(type="str"),
|
||||
force=dict(type="bool", default=True),
|
||||
executable=dict(type="str", default="podman"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[
|
||||
('container', 'volume'),
|
||||
("container", "volume"),
|
||||
],
|
||||
required_one_of=[
|
||||
('container', 'volume'),
|
||||
("container", "volume"),
|
||||
],
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
changed, out, err = export(module, executable)
|
||||
|
||||
results = {
|
||||
|
|
@ -123,5 +125,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
# 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)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
module: podman_generate_systemd
|
||||
author:
|
||||
- Sébastien Gendre (@CyberFox001)
|
||||
|
|
@ -140,9 +141,9 @@ notes:
|
|||
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 = '''
|
||||
EXAMPLES = """
|
||||
# Example of creating a container and systemd unit file.
|
||||
# When using podman_generate_systemd with new:true then
|
||||
# the container needs rm:true for idempotence.
|
||||
|
|
@ -204,9 +205,9 @@ EXAMPLES = '''
|
|||
POSTGRES_USER: my_app
|
||||
POSTGRES_PASSWORD: example
|
||||
register: postgres_local_systemd_unit
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
RETURN = """
|
||||
systemd_units:
|
||||
description: A copy of the generated systemd .service unit(s)
|
||||
returned: always
|
||||
|
|
@ -220,27 +221,29 @@ podman_command:
|
|||
returned: always
|
||||
type: str
|
||||
sample: "podman generate systemd my_webapp"
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import json
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import compare_systemd_file_content
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
compare_systemd_file_content,
|
||||
)
|
||||
|
||||
RESTART_POLICY_CHOICES = [
|
||||
'no-restart',
|
||||
'on-success',
|
||||
'on-failure',
|
||||
'on-abnormal',
|
||||
'on-watchdog',
|
||||
'on-abort',
|
||||
'always',
|
||||
"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.
|
||||
"""Generate systemd .service unit file from a pod or container.
|
||||
|
||||
Parameter:
|
||||
- module (AnsibleModule): An AnsibleModule object
|
||||
|
|
@ -249,7 +252,7 @@ def generate_systemd(module):
|
|||
- 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
|
||||
|
||||
|
|
@ -257,115 +260,115 @@ def generate_systemd(module):
|
|||
command_options = []
|
||||
|
||||
# New option
|
||||
if module.params['new']:
|
||||
command_options.append('--new')
|
||||
if module.params["new"]:
|
||||
command_options.append("--new")
|
||||
|
||||
# Restart policy option
|
||||
restart_policy = module.params['restart_policy']
|
||||
restart_policy = module.params["restart_policy"]
|
||||
if restart_policy:
|
||||
# add the restart policy to options
|
||||
if restart_policy == 'no-restart':
|
||||
restart_policy = 'no'
|
||||
if restart_policy == "no-restart":
|
||||
restart_policy = "no"
|
||||
command_options.append(
|
||||
'--restart-policy={restart_policy}'.format(
|
||||
"--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']
|
||||
restart_sec = module.params["restart_sec"]
|
||||
if restart_sec:
|
||||
command_options.append(
|
||||
'--restart-sec={restart_sec}'.format(
|
||||
"--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']
|
||||
start_timeout = module.params["start_timeout"]
|
||||
if start_timeout:
|
||||
command_options.append(
|
||||
'--start-timeout={start_timeout}'.format(
|
||||
"--start-timeout={start_timeout}".format(
|
||||
start_timeout=start_timeout,
|
||||
),
|
||||
)
|
||||
|
||||
# Stop-timeout option
|
||||
stop_timeout = module.params['stop_timeout']
|
||||
stop_timeout = module.params["stop_timeout"]
|
||||
if stop_timeout:
|
||||
command_options.append(
|
||||
'--stop-timeout={stop_timeout}'.format(
|
||||
"--stop-timeout={stop_timeout}".format(
|
||||
stop_timeout=stop_timeout,
|
||||
),
|
||||
)
|
||||
|
||||
# Use container name(s) option
|
||||
if module.params['use_names']:
|
||||
command_options.append('--name')
|
||||
if module.params["use_names"]:
|
||||
command_options.append("--name")
|
||||
|
||||
# Container-prefix option
|
||||
container_prefix = module.params['container_prefix']
|
||||
container_prefix = module.params["container_prefix"]
|
||||
if container_prefix is not None:
|
||||
command_options.append(
|
||||
'--container-prefix={container_prefix}'.format(
|
||||
"--container-prefix={container_prefix}".format(
|
||||
container_prefix=container_prefix,
|
||||
),
|
||||
)
|
||||
|
||||
# Pod-prefix option
|
||||
pod_prefix = module.params['pod_prefix']
|
||||
pod_prefix = module.params["pod_prefix"]
|
||||
if pod_prefix is not None:
|
||||
command_options.append(
|
||||
'--pod-prefix={pod_prefix}'.format(
|
||||
"--pod-prefix={pod_prefix}".format(
|
||||
pod_prefix=pod_prefix,
|
||||
),
|
||||
)
|
||||
|
||||
# Separator option
|
||||
separator = module.params['separator']
|
||||
separator = module.params["separator"]
|
||||
if separator is not None:
|
||||
command_options.append(
|
||||
'--separator={separator}'.format(
|
||||
"--separator={separator}".format(
|
||||
separator=separator,
|
||||
),
|
||||
)
|
||||
|
||||
# No-header option
|
||||
if module.params['no_header']:
|
||||
command_options.append('--no-header')
|
||||
if module.params["no_header"]:
|
||||
command_options.append("--no-header")
|
||||
|
||||
# After option (only for Podman 4.0.0 and above)
|
||||
after = module.params['after']
|
||||
after = module.params["after"]
|
||||
if after:
|
||||
for item in after:
|
||||
command_options.append(
|
||||
'--after={item}'.format(
|
||||
"--after={item}".format(
|
||||
item=item,
|
||||
),
|
||||
)
|
||||
|
||||
# Wants option (only for Podman 4.0.0 and above)
|
||||
wants = module.params['wants']
|
||||
wants = module.params["wants"]
|
||||
if wants:
|
||||
for item in wants:
|
||||
command_options.append(
|
||||
'--wants={item}'.format(
|
||||
"--wants={item}".format(
|
||||
item=item,
|
||||
)
|
||||
)
|
||||
|
||||
# Requires option (only for Podman 4.0.0 and above)
|
||||
requires = module.params['requires']
|
||||
requires = module.params["requires"]
|
||||
if requires:
|
||||
for item in requires:
|
||||
command_options.append(
|
||||
'--requires={item}'.format(
|
||||
"--requires={item}".format(
|
||||
item=item,
|
||||
),
|
||||
)
|
||||
|
||||
# Environment variables (only for Podman 4.3.0 and above)
|
||||
environment_variables = module.params['env']
|
||||
environment_variables = module.params["env"]
|
||||
if environment_variables:
|
||||
for env_var_name, env_var_value in environment_variables.items():
|
||||
command_options.append(
|
||||
|
|
@ -376,19 +379,21 @@ def generate_systemd(module):
|
|||
)
|
||||
|
||||
# Set output format, of podman command, to json
|
||||
command_options.extend(['--format', 'json'])
|
||||
command_options.extend(["--format", "json"])
|
||||
|
||||
# Full command build, with option included
|
||||
# Base of the command
|
||||
command = [
|
||||
module.params['executable'], 'generate', 'systemd',
|
||||
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'])
|
||||
command.append(module.params["name"])
|
||||
# Build the string version of the command, only for module return
|
||||
command_str = ' '.join(command)
|
||||
command_str = " ".join(command)
|
||||
|
||||
# Run the podman command to generated systemd .service unit(s) content
|
||||
return_code, stdout, stderr = module.run_command(command)
|
||||
|
|
@ -396,10 +401,10 @@ def generate_systemd(module):
|
|||
# In case of error in running the command
|
||||
if return_code != 0:
|
||||
# Print information 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}.'
|
||||
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,
|
||||
|
|
@ -421,9 +426,9 @@ def generate_systemd(module):
|
|||
|
||||
# Write the systemd .service unit(s) content to file(s), if
|
||||
# requested
|
||||
if module.params['dest']:
|
||||
if module.params["dest"]:
|
||||
try:
|
||||
systemd_units_dest = module.params['dest']
|
||||
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
|
||||
|
|
@ -447,23 +452,24 @@ def generate_systemd(module):
|
|||
# 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_name = unit_name + ".service"
|
||||
unit_file_full_path = os.path.join(
|
||||
systemd_units_dest,
|
||||
unit_file_name,
|
||||
)
|
||||
|
||||
if module.params['force']:
|
||||
if module.params["force"]:
|
||||
# Force to replace the existing unit file
|
||||
need_to_write_file = True
|
||||
else:
|
||||
# See if we need to write the unit file, default yes
|
||||
need_to_write_file = bool(compare_systemd_file_content(
|
||||
unit_file_full_path, unit_content))
|
||||
need_to_write_file = bool(
|
||||
compare_systemd_file_content(unit_file_full_path, unit_content)
|
||||
)
|
||||
|
||||
# Write the file, if needed
|
||||
if need_to_write_file:
|
||||
with open(unit_file_full_path, 'w') as unit_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)
|
||||
|
|
@ -471,133 +477,128 @@ def generate_systemd(module):
|
|||
|
||||
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}'
|
||||
message = "PODMAN-GENERATE-SYSTEMD-DEBUG: "
|
||||
message += "Error writing systemd units files: "
|
||||
message += "{exception}"
|
||||
module.log(
|
||||
message.format(
|
||||
exception=exception
|
||||
),
|
||||
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'''
|
||||
"""Run the module on the target"""
|
||||
# Build the list of parameters user can use
|
||||
module_parameters = {
|
||||
'name': {
|
||||
'type': 'str',
|
||||
'required': True,
|
||||
"name": {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
},
|
||||
'dest': {
|
||||
'type': 'path',
|
||||
'required': False,
|
||||
"dest": {
|
||||
"type": "path",
|
||||
"required": False,
|
||||
},
|
||||
'new': {
|
||||
'type': 'bool',
|
||||
'required': False,
|
||||
'default': False,
|
||||
"new": {
|
||||
"type": "bool",
|
||||
"required": False,
|
||||
"default": False,
|
||||
},
|
||||
'force': {
|
||||
'type': 'bool',
|
||||
'required': False,
|
||||
'default': False,
|
||||
"force": {
|
||||
"type": "bool",
|
||||
"required": False,
|
||||
"default": False,
|
||||
},
|
||||
'restart_policy': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
'choices': RESTART_POLICY_CHOICES,
|
||||
"restart_policy": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
"choices": RESTART_POLICY_CHOICES,
|
||||
},
|
||||
'restart_sec': {
|
||||
'type': 'int',
|
||||
'required': False,
|
||||
"restart_sec": {
|
||||
"type": "int",
|
||||
"required": False,
|
||||
},
|
||||
'start_timeout': {
|
||||
'type': 'int',
|
||||
'required': False,
|
||||
"start_timeout": {
|
||||
"type": "int",
|
||||
"required": False,
|
||||
},
|
||||
'stop_timeout': {
|
||||
'type': 'int',
|
||||
'required': False,
|
||||
"stop_timeout": {
|
||||
"type": "int",
|
||||
"required": False,
|
||||
},
|
||||
'env': {
|
||||
'type': 'dict',
|
||||
'required': False,
|
||||
"env": {
|
||||
"type": "dict",
|
||||
"required": False,
|
||||
},
|
||||
'use_names': {
|
||||
'type': 'bool',
|
||||
'required': False,
|
||||
'default': True,
|
||||
"use_names": {
|
||||
"type": "bool",
|
||||
"required": False,
|
||||
"default": True,
|
||||
},
|
||||
'container_prefix': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
"container_prefix": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
},
|
||||
'pod_prefix': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
"pod_prefix": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
},
|
||||
'separator': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
"separator": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
},
|
||||
'no_header': {
|
||||
'type': 'bool',
|
||||
'required': False,
|
||||
'default': False,
|
||||
"no_header": {
|
||||
"type": "bool",
|
||||
"required": False,
|
||||
"default": False,
|
||||
},
|
||||
'after': {
|
||||
'type': 'list',
|
||||
'elements': 'str',
|
||||
'required': False,
|
||||
"after": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"required": False,
|
||||
},
|
||||
'wants': {
|
||||
'type': 'list',
|
||||
'elements': 'str',
|
||||
'required': False,
|
||||
"wants": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"required": False,
|
||||
},
|
||||
'requires': {
|
||||
'type': 'list',
|
||||
'elements': 'str',
|
||||
'required': False,
|
||||
"requires": {
|
||||
"type": "list",
|
||||
"elements": "str",
|
||||
"required": False,
|
||||
},
|
||||
'executable': {
|
||||
'type': 'str',
|
||||
'required': False,
|
||||
'default': 'podman',
|
||||
"executable": {
|
||||
"type": "str",
|
||||
"required": False,
|
||||
"default": "podman",
|
||||
},
|
||||
}
|
||||
|
||||
# Build result dictionary
|
||||
result = {
|
||||
'changed': False,
|
||||
'systemd_units': {},
|
||||
'podman_command': '',
|
||||
"changed": False,
|
||||
"systemd_units": {},
|
||||
"podman_command": "",
|
||||
}
|
||||
|
||||
# Build the Ansible Module
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_parameters,
|
||||
supports_check_mode=True
|
||||
)
|
||||
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
|
||||
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.'''
|
||||
"""Main function of this script."""
|
||||
run_module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_image
|
||||
author:
|
||||
- Sam Doran (@samdoran)
|
||||
|
|
@ -225,7 +226,7 @@ DOCUMENTATION = r'''
|
|||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Pull an image
|
||||
|
|
@ -436,8 +437,12 @@ import sys # noqa: E402
|
|||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import run_podman_command
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
run_podman_command,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import (
|
||||
create_quadlet_state,
|
||||
)
|
||||
|
||||
|
||||
class PodmanImageManager(object):
|
||||
|
|
@ -448,57 +453,68 @@ class PodmanImageManager(object):
|
|||
|
||||
self.module = module
|
||||
self.results = results
|
||||
self.name = self.module.params.get('name')
|
||||
self.executable = self.module.get_bin_path(module.params.get('executable'), required=True)
|
||||
self.tag = self.module.params.get('tag')
|
||||
self.pull = self.module.params.get('pull')
|
||||
self.pull_extra_args = self.module.params.get('pull_extra_args')
|
||||
self.push = self.module.params.get('push')
|
||||
self.path = self.module.params.get('path')
|
||||
self.force = self.module.params.get('force')
|
||||
self.state = self.module.params.get('state')
|
||||
self.validate_certs = self.module.params.get('validate_certs')
|
||||
self.auth_file = self.module.params.get('auth_file')
|
||||
self.username = self.module.params.get('username')
|
||||
self.password = self.module.params.get('password')
|
||||
self.ca_cert_dir = self.module.params.get('ca_cert_dir')
|
||||
self.build = self.module.params.get('build')
|
||||
self.push_args = self.module.params.get('push_args')
|
||||
self.arch = self.module.params.get('arch')
|
||||
self.name = self.module.params.get("name")
|
||||
self.executable = self.module.get_bin_path(
|
||||
module.params.get("executable"), required=True
|
||||
)
|
||||
self.tag = self.module.params.get("tag")
|
||||
self.pull = self.module.params.get("pull")
|
||||
self.pull_extra_args = self.module.params.get("pull_extra_args")
|
||||
self.push = self.module.params.get("push")
|
||||
self.path = self.module.params.get("path")
|
||||
self.force = self.module.params.get("force")
|
||||
self.state = self.module.params.get("state")
|
||||
self.validate_certs = self.module.params.get("validate_certs")
|
||||
self.auth_file = self.module.params.get("auth_file")
|
||||
self.username = self.module.params.get("username")
|
||||
self.password = self.module.params.get("password")
|
||||
self.ca_cert_dir = self.module.params.get("ca_cert_dir")
|
||||
self.build = self.module.params.get("build")
|
||||
self.push_args = self.module.params.get("push_args")
|
||||
self.arch = self.module.params.get("arch")
|
||||
|
||||
repo, repo_tag = parse_repository_tag(self.name)
|
||||
if repo_tag:
|
||||
self.name = repo
|
||||
self.tag = repo_tag
|
||||
|
||||
delimiter = ':' if "sha256" not in self.tag else '@'
|
||||
self.image_name = '{name}{d}{tag}'.format(name=self.name, d=delimiter, tag=self.tag)
|
||||
delimiter = ":" if "sha256" not in self.tag else "@"
|
||||
self.image_name = "{name}{d}{tag}".format(
|
||||
name=self.name, d=delimiter, tag=self.tag
|
||||
)
|
||||
|
||||
if self.state in ['present', 'build']:
|
||||
if self.state in ["present", "build"]:
|
||||
self.present()
|
||||
|
||||
if self.state in ['absent']:
|
||||
if self.state in ["absent"]:
|
||||
self.absent()
|
||||
|
||||
if self.state == 'quadlet':
|
||||
if self.state == "quadlet":
|
||||
self.make_quadlet()
|
||||
|
||||
def _run(self, args, expected_rc=0, ignore_errors=False):
|
||||
cmd = " ".join([self.executable]
|
||||
+ [to_native(i) for i in args])
|
||||
cmd = " ".join([self.executable] + [to_native(i) for i in args])
|
||||
self.module.log("PODMAN-IMAGE-DEBUG: %s" % cmd)
|
||||
self.results['podman_actions'].append(cmd)
|
||||
self.results["podman_actions"].append(cmd)
|
||||
return run_podman_command(
|
||||
module=self.module,
|
||||
executable=self.executable,
|
||||
args=args,
|
||||
expected_rc=expected_rc,
|
||||
ignore_errors=ignore_errors)
|
||||
ignore_errors=ignore_errors,
|
||||
)
|
||||
|
||||
def _get_id_from_output(self, lines, startswith=None, contains=None, split_on=' ', maxsplit=1):
|
||||
def _get_id_from_output(
|
||||
self, lines, startswith=None, contains=None, split_on=" ", maxsplit=1
|
||||
):
|
||||
layer_ids = []
|
||||
for line in lines.splitlines():
|
||||
if startswith and line.startswith(startswith) or contains and contains in line:
|
||||
if (
|
||||
startswith
|
||||
and line.startswith(startswith)
|
||||
or contains
|
||||
and contains in line
|
||||
):
|
||||
splitline = line.rsplit(split_on, maxsplit)
|
||||
layer_ids.append(splitline[1])
|
||||
|
||||
|
|
@ -515,7 +531,9 @@ class PodmanImageManager(object):
|
|||
"""
|
||||
|
||||
containerfile_path = None
|
||||
for filename in [os.path.join(self.path, fname) for fname in ["Containerfile", "Dockerfile"]]:
|
||||
for filename in [
|
||||
os.path.join(self.path, fname) for fname in ["Containerfile", "Dockerfile"]
|
||||
]:
|
||||
if os.path.exists(filename):
|
||||
containerfile_path = filename
|
||||
break
|
||||
|
|
@ -534,8 +552,10 @@ class PodmanImageManager(object):
|
|||
locations, return 'None'.
|
||||
"""
|
||||
|
||||
build_file_arg = self.build.get('file') if self.build else None
|
||||
containerfile_contents = self.build.get('container_file') if self.build else None
|
||||
build_file_arg = self.build.get("file") if self.build else None
|
||||
containerfile_contents = (
|
||||
self.build.get("container_file") if self.build else None
|
||||
)
|
||||
|
||||
container_filename = None
|
||||
if build_file_arg:
|
||||
|
|
@ -564,8 +584,7 @@ class PodmanImageManager(object):
|
|||
).hexdigest()
|
||||
else:
|
||||
return hashlib.sha256(
|
||||
containerfile_contents.encode(),
|
||||
usedforsecurity=False
|
||||
containerfile_contents.encode(), usedforsecurity=False
|
||||
).hexdigest()
|
||||
|
||||
def _get_args_containerfile_hash(self):
|
||||
|
|
@ -578,12 +597,14 @@ class PodmanImageManager(object):
|
|||
|
||||
args_containerfile_hash = None
|
||||
|
||||
context_has_containerfile = self.path and self._find_containerfile_from_context()
|
||||
context_has_containerfile = (
|
||||
self.path and self._find_containerfile_from_context()
|
||||
)
|
||||
|
||||
should_hash_args_containerfile = (
|
||||
context_has_containerfile or
|
||||
self.build.get('file') is not None or
|
||||
self.build.get('container_file') is not None
|
||||
context_has_containerfile
|
||||
or self.build.get("file") is not None
|
||||
or self.build.get("container_file") is not None
|
||||
)
|
||||
|
||||
if should_hash_args_containerfile:
|
||||
|
|
@ -599,68 +620,86 @@ class PodmanImageManager(object):
|
|||
args_containerfile_hash = self._get_args_containerfile_hash()
|
||||
|
||||
if image:
|
||||
digest_before = image[0].get('Digest', image[0].get('digest'))
|
||||
labels = image[0].get('Labels') or {}
|
||||
digest_before = image[0].get("Digest", image[0].get("digest"))
|
||||
labels = image[0].get("Labels") or {}
|
||||
if "containerfile.hash" in labels:
|
||||
existing_image_containerfile_hash = labels["containerfile.hash"]
|
||||
else:
|
||||
digest_before = None
|
||||
|
||||
both_hashes_exist_and_differ = (args_containerfile_hash and existing_image_containerfile_hash and
|
||||
args_containerfile_hash != existing_image_containerfile_hash
|
||||
)
|
||||
both_hashes_exist_and_differ = (
|
||||
args_containerfile_hash
|
||||
and existing_image_containerfile_hash
|
||||
and args_containerfile_hash != existing_image_containerfile_hash
|
||||
)
|
||||
|
||||
if not image or self.force or both_hashes_exist_and_differ:
|
||||
if self.state == 'build' or self.path:
|
||||
if self.state == "build" or self.path:
|
||||
# Build the image
|
||||
build_file = self.build.get('file') if self.build else None
|
||||
container_file_txt = self.build.get('container_file') if self.build else None
|
||||
build_file = self.build.get("file") if self.build else None
|
||||
container_file_txt = (
|
||||
self.build.get("container_file") if self.build else None
|
||||
)
|
||||
if build_file and container_file_txt:
|
||||
self.module.fail_json(msg='Cannot specify both build file and container file content!')
|
||||
self.module.fail_json(
|
||||
msg="Cannot specify both build file and container file content!"
|
||||
)
|
||||
if not self.path and build_file:
|
||||
self.path = os.path.dirname(build_file)
|
||||
elif not self.path and not build_file and not container_file_txt:
|
||||
self.module.fail_json(msg='Path to build context or file is required when building an image')
|
||||
self.results['actions'].append('Built image {image_name} from {path}'.format(
|
||||
image_name=self.image_name, path=self.path or 'default context'))
|
||||
self.module.fail_json(
|
||||
msg="Path to build context or file is required when building an image"
|
||||
)
|
||||
self.results["actions"].append(
|
||||
"Built image {image_name} from {path}".format(
|
||||
image_name=self.image_name, path=self.path or "default context"
|
||||
)
|
||||
)
|
||||
if not self.module.check_mode:
|
||||
self.results['image'], self.results['stdout'] = self.build_image(args_containerfile_hash)
|
||||
image = self.results['image']
|
||||
self.results["image"], self.results["stdout"] = self.build_image(
|
||||
args_containerfile_hash
|
||||
)
|
||||
image = self.results["image"]
|
||||
else:
|
||||
# Pull the image
|
||||
self.results['actions'].append('Pulled image {image_name}'.format(image_name=self.image_name))
|
||||
self.results["actions"].append(
|
||||
"Pulled image {image_name}".format(image_name=self.image_name)
|
||||
)
|
||||
if not self.module.check_mode:
|
||||
image = self.results['image'] = self.pull_image()
|
||||
image = self.results["image"] = self.pull_image()
|
||||
|
||||
if not image:
|
||||
image = self.find_image()
|
||||
if not self.module.check_mode:
|
||||
digest_after = image[0].get('Digest', image[0].get('digest'))
|
||||
self.results['changed'] = digest_before != digest_after
|
||||
digest_after = image[0].get("Digest", image[0].get("digest"))
|
||||
self.results["changed"] = digest_before != digest_after
|
||||
else:
|
||||
self.results['changed'] = True
|
||||
self.results["changed"] = True
|
||||
|
||||
if self.push:
|
||||
self.results['image'], output = self.push_image()
|
||||
self.results['stdout'] += "\n" + output
|
||||
if image and not self.results.get('image'):
|
||||
self.results['image'] = image
|
||||
self.results["image"], output = self.push_image()
|
||||
self.results["stdout"] += "\n" + output
|
||||
if image and not self.results.get("image"):
|
||||
self.results["image"] = image
|
||||
|
||||
def absent(self):
|
||||
image = self.find_image()
|
||||
image_id = self.find_image_id()
|
||||
|
||||
if image:
|
||||
self.results['actions'].append('Removed image {name}'.format(name=self.name))
|
||||
self.results['changed'] = True
|
||||
self.results['image']['state'] = 'Deleted'
|
||||
self.results["actions"].append(
|
||||
"Removed image {name}".format(name=self.name)
|
||||
)
|
||||
self.results["changed"] = True
|
||||
self.results["image"]["state"] = "Deleted"
|
||||
if not self.module.check_mode:
|
||||
self.remove_image()
|
||||
elif image_id:
|
||||
self.results['actions'].append(
|
||||
'Removed image with id {id}'.format(id=self.image_name))
|
||||
self.results['changed'] = True
|
||||
self.results['image']['state'] = 'Deleted'
|
||||
self.results["actions"].append(
|
||||
"Removed image with id {id}".format(id=self.image_name)
|
||||
)
|
||||
self.results["changed"] = True
|
||||
self.results["image"]["state"] = "Deleted"
|
||||
if not self.module.check_mode:
|
||||
self.remove_image_id()
|
||||
|
||||
|
|
@ -673,17 +712,21 @@ class PodmanImageManager(object):
|
|||
if image_name is None:
|
||||
image_name = self.image_name
|
||||
# Let's find out if image exists
|
||||
rc, out, err = self._run(['image', 'exists', image_name], ignore_errors=True)
|
||||
rc, out, err = self._run(["image", "exists", image_name], ignore_errors=True)
|
||||
if rc == 0:
|
||||
inspect_json = self.inspect_image(image_name)
|
||||
else:
|
||||
return None
|
||||
args = ['image', 'ls', image_name, '--format', 'json']
|
||||
args = ["image", "ls", image_name, "--format", "json"]
|
||||
rc, images, err = self._run(args, ignore_errors=True)
|
||||
try:
|
||||
images = json.loads(images)
|
||||
except json.decoder.JSONDecodeError:
|
||||
self.module.fail_json(msg='Failed to parse JSON output from podman image ls: {out}'.format(out=images))
|
||||
self.module.fail_json(
|
||||
msg="Failed to parse JSON output from podman image ls: {out}".format(
|
||||
out=images
|
||||
)
|
||||
)
|
||||
if len(images) == 0:
|
||||
return None
|
||||
inspect_json = self.inspect_image(image_name)
|
||||
|
|
@ -692,16 +735,15 @@ class PodmanImageManager(object):
|
|||
return None
|
||||
|
||||
def _is_target_arch(self, inspect_json=None, arch=None):
|
||||
return arch and inspect_json[0]['Architecture'] == arch
|
||||
return arch and inspect_json[0]["Architecture"] == arch
|
||||
|
||||
def find_image_id(self, image_id=None):
|
||||
if image_id is None:
|
||||
# If image id is set as image_name, remove tag
|
||||
image_id = re.sub(':.*$', '', self.image_name)
|
||||
args = ['image', 'ls', '--quiet', '--no-trunc']
|
||||
image_id = re.sub(":.*$", "", self.image_name)
|
||||
args = ["image", "ls", "--quiet", "--no-trunc"]
|
||||
rc, candidates, err = self._run(args, ignore_errors=True)
|
||||
candidates = [re.sub('^sha256:', '', c)
|
||||
for c in str.splitlines(candidates)]
|
||||
candidates = [re.sub("^sha256:", "", c) for c in str.splitlines(candidates)]
|
||||
for c in candidates:
|
||||
if c.startswith(image_id):
|
||||
return image_id
|
||||
|
|
@ -710,12 +752,16 @@ class PodmanImageManager(object):
|
|||
def inspect_image(self, image_name=None):
|
||||
if image_name is None:
|
||||
image_name = self.image_name
|
||||
args = ['inspect', image_name, '--format', 'json']
|
||||
args = ["inspect", image_name, "--format", "json"]
|
||||
rc, image_data, err = self._run(args)
|
||||
try:
|
||||
image_data = json.loads(image_data)
|
||||
except json.decoder.JSONDecodeError:
|
||||
self.module.fail_json(msg='Failed to parse JSON output from podman inspect: {out}'.format(out=image_data))
|
||||
self.module.fail_json(
|
||||
msg="Failed to parse JSON output from podman inspect: {out}".format(
|
||||
out=image_data
|
||||
)
|
||||
)
|
||||
if len(image_data) > 0:
|
||||
return image_data
|
||||
else:
|
||||
|
|
@ -725,26 +771,28 @@ class PodmanImageManager(object):
|
|||
if image_name is None:
|
||||
image_name = self.image_name
|
||||
|
||||
args = ['pull', image_name, '-q']
|
||||
args = ["pull", image_name, "-q"]
|
||||
|
||||
if self.arch:
|
||||
args.extend(['--arch', self.arch])
|
||||
args.extend(["--arch", self.arch])
|
||||
|
||||
if self.auth_file:
|
||||
args.extend(['--authfile', self.auth_file])
|
||||
args.extend(["--authfile", self.auth_file])
|
||||
|
||||
if self.username and self.password:
|
||||
cred_string = '{user}:{password}'.format(user=self.username, password=self.password)
|
||||
args.extend(['--creds', cred_string])
|
||||
cred_string = "{user}:{password}".format(
|
||||
user=self.username, password=self.password
|
||||
)
|
||||
args.extend(["--creds", cred_string])
|
||||
|
||||
if self.validate_certs is not None:
|
||||
if self.validate_certs:
|
||||
args.append('--tls-verify')
|
||||
args.append("--tls-verify")
|
||||
else:
|
||||
args.append('--tls-verify=false')
|
||||
args.append("--tls-verify=false")
|
||||
|
||||
if self.ca_cert_dir:
|
||||
args.extend(['--cert-dir', self.ca_cert_dir])
|
||||
args.extend(["--cert-dir", self.ca_cert_dir])
|
||||
|
||||
if self.pull_extra_args:
|
||||
args.extend(shlex.split(self.pull_extra_args))
|
||||
|
|
@ -753,177 +801,212 @@ class PodmanImageManager(object):
|
|||
if rc != 0:
|
||||
if not self.pull:
|
||||
self.module.fail_json(
|
||||
msg='Failed to find image {image_name} locally, image pull set to {pull_bool}'.format(
|
||||
pull_bool=self.pull, image_name=image_name))
|
||||
msg="Failed to find image {image_name} locally, image pull set to {pull_bool}".format(
|
||||
pull_bool=self.pull, image_name=image_name
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.module.fail_json(
|
||||
msg='Failed to pull image {image_name}'.format(image_name=image_name))
|
||||
msg="Failed to pull image {image_name}".format(
|
||||
image_name=image_name
|
||||
)
|
||||
)
|
||||
return self.inspect_image(out.strip())
|
||||
|
||||
def build_image(self, containerfile_hash):
|
||||
args = ['build']
|
||||
args.extend(['-t', self.image_name])
|
||||
args = ["build"]
|
||||
args.extend(["-t", self.image_name])
|
||||
|
||||
if self.validate_certs is not None:
|
||||
if self.validate_certs:
|
||||
args.append('--tls-verify')
|
||||
args.append("--tls-verify")
|
||||
else:
|
||||
args.append('--tls-verify=false')
|
||||
args.append("--tls-verify=false")
|
||||
|
||||
annotation = self.build.get('annotation')
|
||||
annotation = self.build.get("annotation")
|
||||
if annotation:
|
||||
for k, v in annotation.items():
|
||||
args.extend(['--annotation', '{k}={v}'.format(k=k, v=v)])
|
||||
args.extend(["--annotation", "{k}={v}".format(k=k, v=v)])
|
||||
|
||||
if self.ca_cert_dir:
|
||||
args.extend(['--cert-dir', self.ca_cert_dir])
|
||||
args.extend(["--cert-dir", self.ca_cert_dir])
|
||||
|
||||
if self.build.get('force_rm'):
|
||||
args.append('--force-rm')
|
||||
if self.build.get("force_rm"):
|
||||
args.append("--force-rm")
|
||||
|
||||
image_format = self.build.get('format')
|
||||
image_format = self.build.get("format")
|
||||
if image_format:
|
||||
args.extend(['--format', image_format])
|
||||
args.extend(["--format", image_format])
|
||||
|
||||
if self.arch:
|
||||
args.extend(['--arch', self.arch])
|
||||
args.extend(["--arch", self.arch])
|
||||
|
||||
if not self.build.get('cache'):
|
||||
args.append('--no-cache')
|
||||
if not self.build.get("cache"):
|
||||
args.append("--no-cache")
|
||||
|
||||
if self.build.get('rm'):
|
||||
args.append('--rm')
|
||||
if self.build.get("rm"):
|
||||
args.append("--rm")
|
||||
|
||||
containerfile = self.build.get('file')
|
||||
containerfile = self.build.get("file")
|
||||
if containerfile:
|
||||
args.extend(['--file', containerfile])
|
||||
container_file_txt = self.build.get('container_file')
|
||||
args.extend(["--file", containerfile])
|
||||
container_file_txt = self.build.get("container_file")
|
||||
if container_file_txt:
|
||||
# create a temporarly file with the content of the Containerfile
|
||||
if self.path:
|
||||
container_file_path = os.path.join(self.path, 'Containerfile.generated_by_ansible_%s' % time.time())
|
||||
container_file_path = os.path.join(
|
||||
self.path, "Containerfile.generated_by_ansible_%s" % time.time()
|
||||
)
|
||||
else:
|
||||
container_file_path = os.path.join(
|
||||
tempfile.gettempdir(), 'Containerfile.generated_by_ansible_%s' % time.time())
|
||||
with open(container_file_path, 'w') as f:
|
||||
tempfile.gettempdir(),
|
||||
"Containerfile.generated_by_ansible_%s" % time.time(),
|
||||
)
|
||||
with open(container_file_path, "w") as f:
|
||||
f.write(container_file_txt)
|
||||
args.extend(['--file', container_file_path])
|
||||
args.extend(["--file", container_file_path])
|
||||
|
||||
if containerfile_hash:
|
||||
args.extend(['--label', f"containerfile.hash={containerfile_hash}"])
|
||||
args.extend(["--label", f"containerfile.hash={containerfile_hash}"])
|
||||
|
||||
volume = self.build.get('volume')
|
||||
volume = self.build.get("volume")
|
||||
if volume:
|
||||
for v in volume:
|
||||
if v:
|
||||
args.extend(['--volume', v])
|
||||
args.extend(["--volume", v])
|
||||
|
||||
if self.auth_file:
|
||||
args.extend(['--authfile', self.auth_file])
|
||||
args.extend(["--authfile", self.auth_file])
|
||||
|
||||
if self.username and self.password:
|
||||
cred_string = '{user}:{password}'.format(user=self.username, password=self.password)
|
||||
args.extend(['--creds', cred_string])
|
||||
cred_string = "{user}:{password}".format(
|
||||
user=self.username, password=self.password
|
||||
)
|
||||
args.extend(["--creds", cred_string])
|
||||
|
||||
extra_args = self.build.get('extra_args')
|
||||
extra_args = self.build.get("extra_args")
|
||||
if extra_args:
|
||||
args.extend(shlex.split(extra_args))
|
||||
|
||||
target = self.build.get('target')
|
||||
target = self.build.get("target")
|
||||
if target:
|
||||
args.extend(['--target', target])
|
||||
args.extend(["--target", target])
|
||||
if self.path:
|
||||
args.append(self.path)
|
||||
|
||||
rc, out, err = self._run(args, ignore_errors=True)
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg="Failed to build image {image}: {out} {err}".format(
|
||||
image=self.image_name, out=out, err=err))
|
||||
self.module.fail_json(
|
||||
msg="Failed to build image {image}: {out} {err}".format(
|
||||
image=self.image_name, out=out, err=err
|
||||
)
|
||||
)
|
||||
# remove the temporary file if it was created
|
||||
if container_file_txt:
|
||||
os.remove(container_file_path)
|
||||
last_id = self._get_id_from_output(out, startswith='-->')
|
||||
last_id = self._get_id_from_output(out, startswith="-->")
|
||||
return self.inspect_image(last_id), out + err
|
||||
|
||||
def push_image(self):
|
||||
args = ['push']
|
||||
args = ["push"]
|
||||
|
||||
if self.validate_certs is not None:
|
||||
if self.validate_certs:
|
||||
args.append('--tls-verify')
|
||||
args.append("--tls-verify")
|
||||
else:
|
||||
args.append('--tls-verify=false')
|
||||
args.append("--tls-verify=false")
|
||||
|
||||
if self.ca_cert_dir:
|
||||
args.extend(['--cert-dir', self.ca_cert_dir])
|
||||
args.extend(["--cert-dir", self.ca_cert_dir])
|
||||
|
||||
if self.username and self.password:
|
||||
cred_string = '{user}:{password}'.format(user=self.username, password=self.password)
|
||||
args.extend(['--creds', cred_string])
|
||||
cred_string = "{user}:{password}".format(
|
||||
user=self.username, password=self.password
|
||||
)
|
||||
args.extend(["--creds", cred_string])
|
||||
|
||||
if self.auth_file:
|
||||
args.extend(['--authfile', self.auth_file])
|
||||
args.extend(["--authfile", self.auth_file])
|
||||
|
||||
if self.push_args.get('compress'):
|
||||
args.append('--compress')
|
||||
if self.push_args.get("compress"):
|
||||
args.append("--compress")
|
||||
|
||||
push_format = self.push_args.get('format')
|
||||
push_format = self.push_args.get("format")
|
||||
if push_format:
|
||||
args.extend(['--format', push_format])
|
||||
args.extend(["--format", push_format])
|
||||
|
||||
if self.push_args.get('remove_signatures'):
|
||||
args.append('--remove-signatures')
|
||||
if self.push_args.get("remove_signatures"):
|
||||
args.append("--remove-signatures")
|
||||
|
||||
sign_by_key = self.push_args.get('sign_by')
|
||||
sign_by_key = self.push_args.get("sign_by")
|
||||
if sign_by_key:
|
||||
args.extend(['--sign-by', sign_by_key])
|
||||
args.extend(["--sign-by", sign_by_key])
|
||||
|
||||
push_extra_args = self.push_args.get('extra_args')
|
||||
push_extra_args = self.push_args.get("extra_args")
|
||||
if push_extra_args:
|
||||
args.extend(shlex.split(push_extra_args))
|
||||
|
||||
args.append(self.image_name)
|
||||
|
||||
# Build the destination argument
|
||||
dest = self.push_args.get('dest')
|
||||
transport = self.push_args.get('transport')
|
||||
dest = self.push_args.get("dest")
|
||||
transport = self.push_args.get("transport")
|
||||
|
||||
if dest is None:
|
||||
dest = self.image_name
|
||||
|
||||
if transport:
|
||||
if transport == 'docker':
|
||||
dest_format_string = '{transport}://{dest}'
|
||||
elif transport == 'ostree':
|
||||
dest_format_string = '{transport}:{name}@{dest}'
|
||||
if transport == "docker":
|
||||
dest_format_string = "{transport}://{dest}"
|
||||
elif transport == "ostree":
|
||||
dest_format_string = "{transport}:{name}@{dest}"
|
||||
else:
|
||||
dest_format_string = '{transport}:{dest}'
|
||||
if transport == 'docker-daemon' and ":" not in dest:
|
||||
dest_format_string = '{transport}:{dest}:latest'
|
||||
dest_string = dest_format_string.format(transport=transport, name=self.name, dest=dest)
|
||||
dest_format_string = "{transport}:{dest}"
|
||||
if transport == "docker-daemon" and ":" not in dest:
|
||||
dest_format_string = "{transport}:{dest}:latest"
|
||||
dest_string = dest_format_string.format(
|
||||
transport=transport, name=self.name, dest=dest
|
||||
)
|
||||
else:
|
||||
dest_string = dest
|
||||
# In case of dest as a repository with org name only, append image name to it
|
||||
if ":" not in dest and "@" not in dest and len(dest.rstrip("/").split("/")) == 2:
|
||||
if (
|
||||
":" not in dest
|
||||
and "@" not in dest
|
||||
and len(dest.rstrip("/").split("/")) == 2
|
||||
):
|
||||
dest_string = dest.rstrip("/") + "/" + self.image_name
|
||||
|
||||
if "/" not in dest_string and "@" not in dest_string and "docker-daemon" not in dest_string:
|
||||
self.module.fail_json(msg="Destination must be a full URL or path to a directory with image name and tag.")
|
||||
if (
|
||||
"/" not in dest_string
|
||||
and "@" not in dest_string
|
||||
and "docker-daemon" not in dest_string
|
||||
):
|
||||
self.module.fail_json(
|
||||
msg="Destination must be a full URL or path to a directory with image name and tag."
|
||||
)
|
||||
|
||||
args.append(dest_string)
|
||||
self.module.log("PODMAN-IMAGE-DEBUG: Pushing image {image_name} to {dest_string}".format(
|
||||
image_name=self.image_name, dest_string=dest_string))
|
||||
self.results['actions'].append(" ".join(args))
|
||||
self.results['changed'] = True
|
||||
out, err = '', ''
|
||||
self.module.log(
|
||||
"PODMAN-IMAGE-DEBUG: Pushing image {image_name} to {dest_string}".format(
|
||||
image_name=self.image_name, dest_string=dest_string
|
||||
)
|
||||
)
|
||||
self.results["actions"].append(" ".join(args))
|
||||
self.results["changed"] = True
|
||||
out, err = "", ""
|
||||
if not self.module.check_mode:
|
||||
rc, out, err = self._run(args, ignore_errors=True)
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg="Failed to push image {image_name}".format(
|
||||
image_name=self.image_name),
|
||||
stdout=out, stderr=err,
|
||||
actions=self.results['actions'],
|
||||
podman_actions=self.results['podman_actions'])
|
||||
self.module.fail_json(
|
||||
msg="Failed to push image {image_name}".format(
|
||||
image_name=self.image_name
|
||||
),
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
actions=self.results["actions"],
|
||||
podman_actions=self.results["podman_actions"],
|
||||
)
|
||||
|
||||
return self.inspect_image(self.image_name), out + err
|
||||
|
||||
|
|
@ -931,35 +1014,41 @@ class PodmanImageManager(object):
|
|||
if image_name is None:
|
||||
image_name = self.image_name
|
||||
|
||||
args = ['rmi', image_name]
|
||||
args = ["rmi", image_name]
|
||||
if self.force:
|
||||
args.append('--force')
|
||||
args.append("--force")
|
||||
rc, out, err = self._run(args, ignore_errors=True)
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg='Failed to remove image {image_name}. {err}'.format(
|
||||
image_name=image_name, err=err))
|
||||
self.module.fail_json(
|
||||
msg="Failed to remove image {image_name}. {err}".format(
|
||||
image_name=image_name, err=err
|
||||
)
|
||||
)
|
||||
return out
|
||||
|
||||
def remove_image_id(self, image_id=None):
|
||||
if image_id is None:
|
||||
image_id = re.sub(':.*$', '', self.image_name)
|
||||
image_id = re.sub(":.*$", "", self.image_name)
|
||||
|
||||
args = ['rmi', image_id]
|
||||
args = ["rmi", image_id]
|
||||
if self.force:
|
||||
args.append('--force')
|
||||
args.append("--force")
|
||||
rc, out, err = self._run(args, ignore_errors=True)
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg='Failed to remove image with id {image_id}. {err}'.format(
|
||||
image_id=image_id, err=err))
|
||||
self.module.fail_json(
|
||||
msg="Failed to remove image with id {image_id}. {err}".format(
|
||||
image_id=image_id, err=err
|
||||
)
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
def parse_repository_tag(repo_name):
|
||||
parts = repo_name.rsplit('@', 1)
|
||||
parts = repo_name.rsplit("@", 1)
|
||||
if len(parts) == 2:
|
||||
return tuple(parts)
|
||||
parts = repo_name.rsplit(':', 1)
|
||||
if len(parts) == 2 and '/' not in parts[1]:
|
||||
parts = repo_name.rsplit(":", 1)
|
||||
if len(parts) == 2 and "/" not in parts[1]:
|
||||
return tuple(parts)
|
||||
return repo_name, None
|
||||
|
||||
|
|
@ -967,77 +1056,78 @@ def parse_repository_tag(repo_name):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(type='str', required=True),
|
||||
arch=dict(type='str'),
|
||||
tag=dict(type='str', default='latest'),
|
||||
pull=dict(type='bool', default=True),
|
||||
pull_extra_args=dict(type='str'),
|
||||
push=dict(type='bool', default=False),
|
||||
path=dict(type='str'),
|
||||
force=dict(type='bool', default=False),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present', 'build', 'quadlet']),
|
||||
validate_certs=dict(type='bool', aliases=['tlsverify', 'tls_verify']),
|
||||
executable=dict(type='str', default='podman'),
|
||||
auth_file=dict(type='path', aliases=['authfile']),
|
||||
username=dict(type='str'),
|
||||
password=dict(type='str', no_log=True),
|
||||
ca_cert_dir=dict(type='path'),
|
||||
quadlet_dir=dict(type='path', required=False),
|
||||
quadlet_filename=dict(type='str'),
|
||||
quadlet_file_mode=dict(type='raw', required=False),
|
||||
quadlet_options=dict(type='list', elements='str', required=False),
|
||||
name=dict(type="str", required=True),
|
||||
arch=dict(type="str"),
|
||||
tag=dict(type="str", default="latest"),
|
||||
pull=dict(type="bool", default=True),
|
||||
pull_extra_args=dict(type="str"),
|
||||
push=dict(type="bool", default=False),
|
||||
path=dict(type="str"),
|
||||
force=dict(type="bool", default=False),
|
||||
state=dict(
|
||||
type="str",
|
||||
default="present",
|
||||
choices=["absent", "present", "build", "quadlet"],
|
||||
),
|
||||
validate_certs=dict(type="bool", aliases=["tlsverify", "tls_verify"]),
|
||||
executable=dict(type="str", default="podman"),
|
||||
auth_file=dict(type="path", aliases=["authfile"]),
|
||||
username=dict(type="str"),
|
||||
password=dict(type="str", no_log=True),
|
||||
ca_cert_dir=dict(type="path"),
|
||||
quadlet_dir=dict(type="path", required=False),
|
||||
quadlet_filename=dict(type="str"),
|
||||
quadlet_file_mode=dict(type="raw", required=False),
|
||||
quadlet_options=dict(type="list", elements="str", required=False),
|
||||
build=dict(
|
||||
type='dict',
|
||||
aliases=['build_args', 'buildargs'],
|
||||
type="dict",
|
||||
aliases=["build_args", "buildargs"],
|
||||
default={},
|
||||
options=dict(
|
||||
annotation=dict(type='dict'),
|
||||
force_rm=dict(type='bool', default=False),
|
||||
file=dict(type='path'),
|
||||
container_file=dict(type='str'),
|
||||
format=dict(
|
||||
type='str',
|
||||
choices=['oci', 'docker'],
|
||||
default='oci'
|
||||
),
|
||||
cache=dict(type='bool', default=True),
|
||||
rm=dict(type='bool', default=True),
|
||||
volume=dict(type='list', elements='str'),
|
||||
extra_args=dict(type='str'),
|
||||
target=dict(type='str'),
|
||||
annotation=dict(type="dict"),
|
||||
force_rm=dict(type="bool", default=False),
|
||||
file=dict(type="path"),
|
||||
container_file=dict(type="str"),
|
||||
format=dict(type="str", choices=["oci", "docker"], default="oci"),
|
||||
cache=dict(type="bool", default=True),
|
||||
rm=dict(type="bool", default=True),
|
||||
volume=dict(type="list", elements="str"),
|
||||
extra_args=dict(type="str"),
|
||||
target=dict(type="str"),
|
||||
),
|
||||
),
|
||||
push_args=dict(
|
||||
type='dict',
|
||||
type="dict",
|
||||
default={},
|
||||
options=dict(
|
||||
compress=dict(type='bool'),
|
||||
format=dict(type='str', choices=['oci', 'v2s1', 'v2s2']),
|
||||
remove_signatures=dict(type='bool'),
|
||||
sign_by=dict(type='str'),
|
||||
dest=dict(type='str', aliases=['destination'],),
|
||||
extra_args=dict(type='str'),
|
||||
compress=dict(type="bool"),
|
||||
format=dict(type="str", choices=["oci", "v2s1", "v2s2"]),
|
||||
remove_signatures=dict(type="bool"),
|
||||
sign_by=dict(type="str"),
|
||||
dest=dict(
|
||||
type="str",
|
||||
aliases=["destination"],
|
||||
),
|
||||
extra_args=dict(type="str"),
|
||||
transport=dict(
|
||||
type='str',
|
||||
type="str",
|
||||
choices=[
|
||||
'dir',
|
||||
'docker-archive',
|
||||
'docker-daemon',
|
||||
'oci-archive',
|
||||
'ostree',
|
||||
'docker'
|
||||
]
|
||||
"dir",
|
||||
"docker-archive",
|
||||
"docker-daemon",
|
||||
"oci-archive",
|
||||
"ostree",
|
||||
"docker",
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_together=(
|
||||
['username', 'password'],
|
||||
),
|
||||
required_together=(["username", "password"],),
|
||||
mutually_exclusive=(
|
||||
['auth_file', 'username'],
|
||||
['auth_file', 'password'],
|
||||
["auth_file", "username"],
|
||||
["auth_file", "password"],
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -1046,12 +1136,12 @@ def main():
|
|||
actions=[],
|
||||
podman_actions=[],
|
||||
image={},
|
||||
stdout='',
|
||||
stdout="",
|
||||
)
|
||||
|
||||
PodmanImageManager(module, results)
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_image_info
|
||||
author:
|
||||
- Sam Doran (@samdoran)
|
||||
|
|
@ -27,7 +28,7 @@ options:
|
|||
type: list
|
||||
elements: str
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Gather info for all images
|
||||
|
|
@ -126,13 +127,13 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
|
||||
def image_exists(module, executable, name):
|
||||
command = [executable, 'image', 'exists', name]
|
||||
command = [executable, "image", "exists", name]
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc == 1:
|
||||
return False
|
||||
elif 'Command "exists" not found' in err:
|
||||
# The 'exists' test is available in podman >= 0.12.1
|
||||
command = [executable, 'image', 'ls', '-q', name]
|
||||
command = [executable, "image", "ls", "-q", name]
|
||||
rc2, out2, err2 = module.run_command(command)
|
||||
if rc2 != 0:
|
||||
return False
|
||||
|
|
@ -158,12 +159,14 @@ def get_image_info(module, executable, name):
|
|||
names = [name]
|
||||
|
||||
if len(names) > 0:
|
||||
command = [executable, 'image', 'inspect']
|
||||
command = [executable, "image", "inspect"]
|
||||
command.extend(names)
|
||||
rc, out, err = module.run_command(command)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Unable to gather info for '{0}': {1}".format(', '.join(names), err))
|
||||
module.fail_json(
|
||||
msg="Unable to gather info for '{0}': {1}".format(", ".join(names), err)
|
||||
)
|
||||
return out
|
||||
|
||||
else:
|
||||
|
|
@ -171,11 +174,11 @@ def get_image_info(module, executable, name):
|
|||
|
||||
|
||||
def get_all_image_info(module, executable):
|
||||
command = [executable, 'image', 'ls', '-q']
|
||||
command = [executable, "image", "ls", "-q"]
|
||||
rc, out, err = module.run_command(command)
|
||||
out = out.strip()
|
||||
if out:
|
||||
name = out.split('\n')
|
||||
name = out.split("\n")
|
||||
res = get_image_info(module, executable, name)
|
||||
return res
|
||||
return json.dumps([])
|
||||
|
|
@ -184,14 +187,14 @@ def get_all_image_info(module, executable):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
name=dict(type='list', elements='str')
|
||||
executable=dict(type="str", default="podman"),
|
||||
name=dict(type="list", elements="str"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
executable = module.params['executable']
|
||||
name = module.params.get('name')
|
||||
executable = module.params["executable"]
|
||||
name = module.params.get("name")
|
||||
executable = module.get_bin_path(executable, required=True)
|
||||
|
||||
if name:
|
||||
|
|
@ -200,13 +203,10 @@ def main():
|
|||
else:
|
||||
results = json.loads(get_all_image_info(module, executable))
|
||||
|
||||
results = dict(
|
||||
changed=False,
|
||||
images=results
|
||||
)
|
||||
results = dict(changed=False, images=results)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_import
|
||||
short_description: Import Podman container from a tar file.
|
||||
author: Sagi Shnaidman (@sshnaidm)
|
||||
|
|
@ -41,9 +41,9 @@ options:
|
|||
type: str
|
||||
requirements:
|
||||
- "Podman installed on host"
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
RETURN = """
|
||||
image:
|
||||
description: info from loaded image
|
||||
returned: always
|
||||
|
|
@ -89,9 +89,9 @@ image:
|
|||
],
|
||||
"NamesHistory": null
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
# What modules does for example
|
||||
- containers.podman.podman_import:
|
||||
src: /path/to/tar/file
|
||||
|
|
@ -102,7 +102,7 @@ EXAMPLES = '''
|
|||
- containers.podman.podman_import:
|
||||
src: /path/to/tar/file
|
||||
volume: myvolume
|
||||
'''
|
||||
"""
|
||||
|
||||
import json # noqa: E402
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
||||
|
|
@ -110,22 +110,22 @@ from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
|||
|
||||
def load(module, executable):
|
||||
changed = False
|
||||
command = [executable, 'import']
|
||||
if module.params['commit_message']:
|
||||
command.extend(['--message', module.params['commit_message']])
|
||||
if module.params['change']:
|
||||
for change in module.params['change']:
|
||||
command += ['--change', "=".join(list(change.items())[0])]
|
||||
command += [module.params['src']]
|
||||
command = [executable, "import"]
|
||||
if module.params["commit_message"]:
|
||||
command.extend(["--message", module.params["commit_message"]])
|
||||
if module.params["change"]:
|
||||
for change in module.params["change"]:
|
||||
command += ["--change", "=".join(list(change.items())[0])]
|
||||
command += [module.params["src"]]
|
||||
changed = True
|
||||
if module.check_mode:
|
||||
return changed, '', '', '', command
|
||||
return changed, "", "", "", command
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Image loading failed: %s" % (err))
|
||||
image_name_line = [i for i in out.splitlines() if 'sha256' in i][0]
|
||||
image_name_line = [i for i in out.splitlines() if "sha256" in i][0]
|
||||
image_name = image_name_line.split(":", maxsplit=1)[1].strip()
|
||||
rc, out2, err2 = module.run_command([executable, 'image', 'inspect', image_name])
|
||||
rc, out2, err2 = module.run_command([executable, "image", "inspect", image_name])
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Image %s inspection failed: %s" % (image_name, err2))
|
||||
try:
|
||||
|
|
@ -137,43 +137,55 @@ def load(module, executable):
|
|||
|
||||
def volume_load(module, executable):
|
||||
changed = True
|
||||
command = [executable, 'volume', 'import', module.params['volume'], module.params['src']]
|
||||
src = module.params['src']
|
||||
command = [
|
||||
executable,
|
||||
"volume",
|
||||
"import",
|
||||
module.params["volume"],
|
||||
module.params["src"],
|
||||
]
|
||||
src = module.params["src"]
|
||||
if module.check_mode:
|
||||
return changed, '', '', '', command
|
||||
return changed, "", "", "", command
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Error importing volume %s: %s" % (src, err))
|
||||
rc, out2, err2 = module.run_command([executable, 'volume', 'inspect', module.params['volume']])
|
||||
rc, out2, err2 = module.run_command(
|
||||
[executable, "volume", "inspect", module.params["volume"]]
|
||||
)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Volume %s inspection failed: %s" % (module.params['volume'], err2))
|
||||
module.fail_json(
|
||||
msg="Volume %s inspection failed: %s" % (module.params["volume"], err2)
|
||||
)
|
||||
try:
|
||||
info = json.loads(out2)[0]
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Could not parse JSON from volume %s: %s" % (module.params['volume'], e))
|
||||
module.fail_json(
|
||||
msg="Could not parse JSON from volume %s: %s" % (module.params["volume"], e)
|
||||
)
|
||||
return changed, out, err, info, command
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
src=dict(type='str', required=True),
|
||||
commit_message=dict(type='str'),
|
||||
change=dict(type='list', elements='dict'),
|
||||
executable=dict(type='str', default='podman'),
|
||||
volume=dict(type='str', required=False),
|
||||
src=dict(type="str", required=True),
|
||||
commit_message=dict(type="str"),
|
||||
change=dict(type="list", elements="dict"),
|
||||
executable=dict(type="str", default="podman"),
|
||||
volume=dict(type="str", required=False),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
('volume', 'commit_message'),
|
||||
('volume', 'change'),
|
||||
("volume", "commit_message"),
|
||||
("volume", "change"),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
volume_info = ''
|
||||
image_info = ''
|
||||
if module.params['volume']:
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
volume_info = ""
|
||||
image_info = ""
|
||||
if module.params["volume"]:
|
||||
changed, out, err, volume_info, command = volume_load(module, executable)
|
||||
else:
|
||||
changed, out, err, image_info, command = load(module, executable)
|
||||
|
|
@ -184,11 +196,11 @@ def main():
|
|||
"stderr": err,
|
||||
"image": image_info,
|
||||
"volume": volume_info,
|
||||
"podman_command": " ".join(command)
|
||||
"podman_command": " ".join(command),
|
||||
}
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_load
|
||||
short_description: Load image from a tar file.
|
||||
author: Sagi Shnaidman (@sshnaidm)
|
||||
|
|
@ -32,9 +32,9 @@ options:
|
|||
type: str
|
||||
requirements:
|
||||
- "Podman installed on host"
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
RETURN = """
|
||||
image:
|
||||
description: info from loaded image
|
||||
returned: always
|
||||
|
|
@ -132,13 +132,13 @@ c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/merged",
|
|||
"VirtualSize": 569919342
|
||||
}
|
||||
]
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
# What modules does for example
|
||||
- containers.podman.podman_load:
|
||||
input: /path/to/tar/file
|
||||
'''
|
||||
"""
|
||||
|
||||
import json # noqa: E402
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
||||
|
|
@ -146,24 +146,24 @@ from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
|||
|
||||
def load(module, executable):
|
||||
changed = False
|
||||
command = [executable, 'load', '--input']
|
||||
command.append(module.params['input'])
|
||||
command = [executable, "load", "--input"]
|
||||
command.append(module.params["input"])
|
||||
changed = True
|
||||
if module.check_mode:
|
||||
return changed, '', '', ''
|
||||
return changed, "", "", ""
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Image loading failed: %s" % (err))
|
||||
image_name_line = [i for i in out.splitlines() if 'Loaded image' in i][0]
|
||||
image_name_line = [i for i in out.splitlines() if "Loaded image" in i][0]
|
||||
# For Podman < 4.x
|
||||
if 'Loaded image(s):' in image_name_line:
|
||||
image_name = image_name_line.split("Loaded image(s): ")[1].split(',')[0].strip()
|
||||
if "Loaded image(s):" in image_name_line:
|
||||
image_name = image_name_line.split("Loaded image(s): ")[1].split(",")[0].strip()
|
||||
# For Podman > 4.x
|
||||
elif 'Loaded image:' in image_name_line:
|
||||
elif "Loaded image:" in image_name_line:
|
||||
image_name = image_name_line.split("Loaded image: ")[1].strip()
|
||||
else:
|
||||
module.fail_json(msg="Not found images in %s" % image_name_line)
|
||||
rc, out2, err2 = module.run_command([executable, 'image', 'inspect', image_name])
|
||||
rc, out2, err2 = module.run_command([executable, "image", "inspect", image_name])
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Image %s inspection failed: %s" % (image_name, err2))
|
||||
try:
|
||||
|
|
@ -176,13 +176,13 @@ def load(module, executable):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
input=dict(type='str', required=True, aliases=['path']),
|
||||
executable=dict(type='str', default='podman')
|
||||
input=dict(type="str", required=True, aliases=["path"]),
|
||||
executable=dict(type="str", default="podman"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
changed, out, err, image_info = load(module, executable)
|
||||
|
||||
results = {
|
||||
|
|
@ -195,5 +195,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_login
|
||||
author:
|
||||
- "Jason Hiatt (@jthiatt)"
|
||||
|
|
@ -70,7 +71,7 @@ options:
|
|||
- Name of an existing C(podman) secret to use for authentication
|
||||
to target registry
|
||||
type: str
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Login to default registry and create ${XDG_RUNTIME_DIR}/containers/auth.json
|
||||
|
|
@ -96,57 +97,70 @@ EXAMPLES = r"""
|
|||
import hashlib
|
||||
import os
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import get_podman_version
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
LooseVersion,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
get_podman_version,
|
||||
)
|
||||
|
||||
|
||||
def login(module, executable, registry, authfile,
|
||||
certdir, tlsverify, username, password, secret):
|
||||
def login(
|
||||
module,
|
||||
executable,
|
||||
registry,
|
||||
authfile,
|
||||
certdir,
|
||||
tlsverify,
|
||||
username,
|
||||
password,
|
||||
secret,
|
||||
):
|
||||
|
||||
command = [executable, 'login']
|
||||
command = [executable, "login"]
|
||||
changed = False
|
||||
|
||||
if username:
|
||||
command.extend(['--username', username])
|
||||
command.extend(["--username", username])
|
||||
if password:
|
||||
command.extend(['--password', password])
|
||||
command.extend(["--password", password])
|
||||
if secret:
|
||||
command.extend(['--secret', secret])
|
||||
command.extend(["--secret", secret])
|
||||
if authfile:
|
||||
command.extend(['--authfile', authfile])
|
||||
command.extend(["--authfile", authfile])
|
||||
authfile = os.path.expandvars(authfile)
|
||||
else:
|
||||
authfile = os.getenv('XDG_RUNTIME_DIR', '') + '/containers/auth.json'
|
||||
authfile = os.getenv("XDG_RUNTIME_DIR", "") + "/containers/auth.json"
|
||||
if registry:
|
||||
command.append(registry)
|
||||
if certdir:
|
||||
command.extend(['--cert-dir', certdir])
|
||||
command.extend(["--cert-dir", certdir])
|
||||
if tlsverify is not None:
|
||||
if tlsverify:
|
||||
command.append('--tls-verify')
|
||||
command.append("--tls-verify")
|
||||
else:
|
||||
command.append('--tls-verify=False')
|
||||
command.append("--tls-verify=False")
|
||||
# Use a checksum to check if the auth JSON has changed
|
||||
checksum = None
|
||||
docker_authfile = os.path.expandvars('$HOME/.docker/config.json')
|
||||
docker_authfile = os.path.expandvars("$HOME/.docker/config.json")
|
||||
# podman falls back to ~/.docker/config.json if the default authfile doesn't exist
|
||||
check_file = authfile if os.path.exists(authfile) else docker_authfile
|
||||
if os.path.exists(check_file):
|
||||
content = open(check_file, 'rb').read()
|
||||
content = open(check_file, "rb").read()
|
||||
checksum = hashlib.sha256(content).hexdigest()
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
if 'Error: Not logged into' not in err:
|
||||
if "Error: Not logged into" not in err:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err))
|
||||
else:
|
||||
# If the command is successful, we managed to login
|
||||
changed = True
|
||||
if 'Existing credentials are valid' in out:
|
||||
if "Existing credentials are valid" in out:
|
||||
changed = False
|
||||
# If we have managed to calculate a checksum before, check if it has changed
|
||||
# due to the login
|
||||
if checksum:
|
||||
content = open(check_file, 'rb').read()
|
||||
content = open(check_file, "rb").read()
|
||||
new_checksum = hashlib.sha256(content).hexdigest()
|
||||
if new_checksum == checksum:
|
||||
changed = False
|
||||
|
|
@ -156,47 +170,56 @@ def login(module, executable, registry, authfile,
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
registry=dict(type='str'),
|
||||
authfile=dict(type='path'),
|
||||
username=dict(type='str'),
|
||||
password=dict(type='str', no_log=True),
|
||||
certdir=dict(type='path'),
|
||||
tlsverify=dict(type='bool'),
|
||||
secret=dict(type='str', no_log=False),
|
||||
executable=dict(type="str", default="podman"),
|
||||
registry=dict(type="str"),
|
||||
authfile=dict(type="path"),
|
||||
username=dict(type="str"),
|
||||
password=dict(type="str", no_log=True),
|
||||
certdir=dict(type="path"),
|
||||
tlsverify=dict(type="bool"),
|
||||
secret=dict(type="str", no_log=False),
|
||||
),
|
||||
supports_check_mode=False,
|
||||
required_by={
|
||||
'password': 'username',
|
||||
"password": "username",
|
||||
},
|
||||
mutually_exclusive=[
|
||||
['password', 'secret'],
|
||||
["password", "secret"],
|
||||
],
|
||||
)
|
||||
|
||||
registry = module.params['registry']
|
||||
authfile = module.params['authfile']
|
||||
username = module.params['username']
|
||||
password = module.params['password']
|
||||
certdir = module.params['certdir']
|
||||
tlsverify = module.params['tlsverify']
|
||||
secret = module.params['secret']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
registry = module.params["registry"]
|
||||
authfile = module.params["authfile"]
|
||||
username = module.params["username"]
|
||||
password = module.params["password"]
|
||||
certdir = module.params["certdir"]
|
||||
tlsverify = module.params["tlsverify"]
|
||||
secret = module.params["secret"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
podman_version = get_podman_version(module, fail=False)
|
||||
|
||||
if (
|
||||
(podman_version is not None) and
|
||||
(LooseVersion(podman_version) < LooseVersion('4.7.0')) and
|
||||
secret
|
||||
(podman_version is not None)
|
||||
and (LooseVersion(podman_version) < LooseVersion("4.7.0"))
|
||||
and secret
|
||||
):
|
||||
module.fail_json(msg="secret option may not be used with podman < 4.7.0")
|
||||
|
||||
if username and ((not password) and (not secret)):
|
||||
module.fail_json(msg="Must pass either password or secret with username")
|
||||
|
||||
changed, out, err = login(module, executable, registry, authfile,
|
||||
certdir, tlsverify, username, password, secret)
|
||||
changed, out, err = login(
|
||||
module,
|
||||
executable,
|
||||
registry,
|
||||
authfile,
|
||||
certdir,
|
||||
tlsverify,
|
||||
username,
|
||||
password,
|
||||
secret,
|
||||
)
|
||||
|
||||
results = {
|
||||
"changed": changed,
|
||||
|
|
@ -207,5 +230,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# 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
|
||||
|
||||
|
||||
|
|
@ -64,23 +65,23 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
|
||||
def get_login_info(module, executable, authfile, registry):
|
||||
command = [executable, 'login', '--get-login']
|
||||
command = [executable, "login", "--get-login"]
|
||||
result = dict(
|
||||
registry=registry,
|
||||
username='',
|
||||
username="",
|
||||
logged_in=False,
|
||||
)
|
||||
if authfile:
|
||||
command.extend(['--authfile', authfile])
|
||||
command.extend(["--authfile", authfile])
|
||||
if registry:
|
||||
command.append(registry)
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
if 'Error: not logged into' in err:
|
||||
if "Error: not logged into" in err:
|
||||
# The error message is e.g. 'Error: not logged into docker.io'
|
||||
# Therefore get last word to extract registry name
|
||||
result["registry"] = err.split()[-1]
|
||||
err = ''
|
||||
err = ""
|
||||
return result
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err))
|
||||
result["username"] = out.strip()
|
||||
|
|
@ -91,16 +92,16 @@ def get_login_info(module, executable, authfile, registry):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
authfile=dict(type='path'),
|
||||
registry=dict(type='str', required=True)
|
||||
executable=dict(type="str", default="podman"),
|
||||
authfile=dict(type="path"),
|
||||
registry=dict(type="str", required=True),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
registry = module.params['registry']
|
||||
authfile = module.params['authfile']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
registry = module.params["registry"]
|
||||
authfile = module.params["authfile"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
inspect_results = get_login_info(module, executable, authfile, registry)
|
||||
|
||||
|
|
@ -112,5 +113,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_logout
|
||||
author:
|
||||
- "Clemens Lange (@clelange)"
|
||||
|
|
@ -63,7 +64,7 @@ options:
|
|||
machine running C(podman)
|
||||
default: 'podman'
|
||||
type: str
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Log out of default registry
|
||||
|
|
@ -87,29 +88,31 @@ EXAMPLES = r"""
|
|||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def logout(module, executable, registry, authfile, all_registries, ignore_docker_credentials):
|
||||
command = [executable, 'logout']
|
||||
def logout(
|
||||
module, executable, registry, authfile, all_registries, ignore_docker_credentials
|
||||
):
|
||||
command = [executable, "logout"]
|
||||
changed = False
|
||||
if authfile:
|
||||
command.extend(['--authfile', authfile])
|
||||
command.extend(["--authfile", authfile])
|
||||
if registry:
|
||||
command.append(registry)
|
||||
if all_registries:
|
||||
command.append("--all")
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
if 'Error: Not logged into' not in err:
|
||||
if "Error: Not logged into" not in err:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err))
|
||||
else:
|
||||
# If the command is successful, we managed to log out
|
||||
# Mind: This also applied if --all flag is used, while in this case
|
||||
# there is no check whether one has been logged into any registry
|
||||
changed = True
|
||||
if 'Existing credentials were established via' in out:
|
||||
if "Existing credentials were established via" in out:
|
||||
# The command will return successfully but not log out the user if the
|
||||
# credentials were initially created using docker. Catch this behaviour:
|
||||
if not ignore_docker_credentials:
|
||||
module.fail_json(msg="Unable to log out %s: %s" % (registry or '', out))
|
||||
module.fail_json(msg="Unable to log out %s: %s" % (registry or "", out))
|
||||
else:
|
||||
changed = False
|
||||
return changed, out, err
|
||||
|
|
@ -118,27 +121,33 @@ def logout(module, executable, registry, authfile, all_registries, ignore_docker
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
registry=dict(type='str'),
|
||||
authfile=dict(type='path'),
|
||||
all=dict(type='bool'),
|
||||
ignore_docker_credentials=dict(type='bool'),
|
||||
executable=dict(type="str", default="podman"),
|
||||
registry=dict(type="str"),
|
||||
authfile=dict(type="path"),
|
||||
all=dict(type="bool"),
|
||||
ignore_docker_credentials=dict(type="bool"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=(
|
||||
['registry', 'all'],
|
||||
['ignore_docker_credentials', 'all'],
|
||||
["registry", "all"],
|
||||
["ignore_docker_credentials", "all"],
|
||||
),
|
||||
)
|
||||
|
||||
registry = module.params['registry']
|
||||
authfile = module.params['authfile']
|
||||
all_registries = module.params['all']
|
||||
ignore_docker_credentials = module.params['ignore_docker_credentials']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
registry = module.params["registry"]
|
||||
authfile = module.params["authfile"]
|
||||
all_registries = module.params["all"]
|
||||
ignore_docker_credentials = module.params["ignore_docker_credentials"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
changed, out, err = logout(module, executable, registry, authfile,
|
||||
all_registries, ignore_docker_credentials)
|
||||
changed, out, err = logout(
|
||||
module,
|
||||
executable,
|
||||
registry,
|
||||
authfile,
|
||||
all_registries,
|
||||
ignore_docker_credentials,
|
||||
)
|
||||
|
||||
results = {
|
||||
"changed": changed,
|
||||
|
|
@ -149,5 +158,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
# 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
|
||||
|
||||
|
||||
|
|
@ -295,27 +296,35 @@ network:
|
|||
"""
|
||||
|
||||
import json
|
||||
|
||||
try:
|
||||
import ipaddress
|
||||
|
||||
HAS_IP_ADDRESS_MODULE = True
|
||||
except ImportError:
|
||||
HAS_IP_ADDRESS_MODULE = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: F402
|
||||
from ansible.module_utils._text import to_bytes, to_native # noqa: F402
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
LooseVersion,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
lower_keys,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import (
|
||||
create_quadlet_state,
|
||||
)
|
||||
|
||||
|
||||
class PodmanNetworkModuleParams:
|
||||
"""Creates list of arguments for podman CLI command.
|
||||
|
||||
Arguments:
|
||||
action {str} -- action type from 'create', 'delete'
|
||||
params {dict} -- dictionary of module parameters
|
||||
Arguments:
|
||||
action {str} -- action type from 'create', 'delete'
|
||||
params {dict} -- dictionary of module parameters
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, action, params, podman_version, module):
|
||||
self.params = params
|
||||
|
|
@ -329,99 +338,104 @@ class PodmanNetworkModuleParams:
|
|||
Returns:
|
||||
list -- list of byte strings for Popen command
|
||||
"""
|
||||
if self.action in ['delete']:
|
||||
if self.action in ["delete"]:
|
||||
return self._delete_action()
|
||||
if self.action in ['create']:
|
||||
if self.action in ["create"]:
|
||||
return self._create_action()
|
||||
|
||||
def _delete_action(self):
|
||||
cmd = ['rm', self.params['name']]
|
||||
if self.params['force']:
|
||||
cmd += ['--force']
|
||||
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
|
||||
cmd = ["rm", self.params["name"]]
|
||||
if self.params["force"]:
|
||||
cmd += ["--force"]
|
||||
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
||||
|
||||
def _create_action(self):
|
||||
cmd = [self.action, self.params['name']]
|
||||
all_param_methods = [func for func in dir(self)
|
||||
if callable(getattr(self, func))
|
||||
and func.startswith("addparam")]
|
||||
cmd = [self.action, self.params["name"]]
|
||||
all_param_methods = [
|
||||
func
|
||||
for func in dir(self)
|
||||
if callable(getattr(self, func)) and func.startswith("addparam")
|
||||
]
|
||||
params_set = (i for i in self.params if self.params[i] is not None)
|
||||
for param in params_set:
|
||||
func_name = "_".join(["addparam", param])
|
||||
if func_name in all_param_methods:
|
||||
cmd = getattr(self, func_name)(cmd)
|
||||
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
|
||||
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
||||
|
||||
def check_version(self, param, minv=None, maxv=None):
|
||||
if minv and LooseVersion(minv) > LooseVersion(
|
||||
self.podman_version):
|
||||
self.module.fail_json(msg="Parameter %s is supported from podman "
|
||||
"version %s only! Current version is %s" % (
|
||||
param, minv, self.podman_version))
|
||||
if maxv and LooseVersion(maxv) < LooseVersion(
|
||||
self.podman_version):
|
||||
self.module.fail_json(msg="Parameter %s is supported till podman "
|
||||
"version %s only! Current version is %s" % (
|
||||
param, minv, self.podman_version))
|
||||
if minv and LooseVersion(minv) > LooseVersion(self.podman_version):
|
||||
self.module.fail_json(
|
||||
msg="Parameter %s is supported from podman "
|
||||
"version %s only! Current version is %s"
|
||||
% (param, minv, self.podman_version)
|
||||
)
|
||||
if maxv and LooseVersion(maxv) < LooseVersion(self.podman_version):
|
||||
self.module.fail_json(
|
||||
msg="Parameter %s is supported till podman "
|
||||
"version %s only! Current version is %s"
|
||||
% (param, minv, self.podman_version)
|
||||
)
|
||||
|
||||
def addparam_gateway(self, c):
|
||||
return c + ['--gateway', self.params['gateway']]
|
||||
return c + ["--gateway", self.params["gateway"]]
|
||||
|
||||
def addparam_dns(self, c):
|
||||
for dns in self.params['dns']:
|
||||
c += ['--dns', dns]
|
||||
for dns in self.params["dns"]:
|
||||
c += ["--dns", dns]
|
||||
return c
|
||||
|
||||
def addparam_driver(self, c):
|
||||
return c + ['--driver', self.params['driver']]
|
||||
return c + ["--driver", self.params["driver"]]
|
||||
|
||||
def addparam_subnet(self, c):
|
||||
return c + ['--subnet', self.params['subnet']]
|
||||
return c + ["--subnet", self.params["subnet"]]
|
||||
|
||||
def addparam_ip_range(self, c):
|
||||
return c + ['--ip-range', self.params['ip_range']]
|
||||
return c + ["--ip-range", self.params["ip_range"]]
|
||||
|
||||
def addparam_ipv6(self, c):
|
||||
return c + ['--ipv6=%s' % self.params['ipv6']]
|
||||
return c + ["--ipv6=%s" % self.params["ipv6"]]
|
||||
|
||||
def addparam_macvlan(self, c):
|
||||
return c + ['--macvlan', self.params['macvlan']]
|
||||
return c + ["--macvlan", self.params["macvlan"]]
|
||||
|
||||
def addparam_net_config(self, c):
|
||||
for net in self.params['net_config']:
|
||||
for kw in ('subnet', 'gateway', 'ip_range'):
|
||||
for net in self.params["net_config"]:
|
||||
for kw in ("subnet", "gateway", "ip_range"):
|
||||
if kw in net and net[kw]:
|
||||
c += ['--%s=%s' % (kw.replace('_', '-'), net[kw])]
|
||||
c += ["--%s=%s" % (kw.replace("_", "-"), net[kw])]
|
||||
return c
|
||||
|
||||
def addparam_interface_name(self, c):
|
||||
return c + ['--interface-name', self.params['interface_name']]
|
||||
return c + ["--interface-name", self.params["interface_name"]]
|
||||
|
||||
def addparam_internal(self, c):
|
||||
return c + ['--internal=%s' % self.params['internal']]
|
||||
return c + ["--internal=%s" % self.params["internal"]]
|
||||
|
||||
def addparam_opt(self, c):
|
||||
for opt in self.params['opt'].items():
|
||||
for opt in self.params["opt"].items():
|
||||
if opt[1] is not None:
|
||||
if opt[0] == 'bridge_name':
|
||||
opt = ('com.docker.network.bridge.name', opt[1])
|
||||
if opt[0] == 'driver_mtu':
|
||||
opt = ('com.docker.network.driver.mtu', opt[1])
|
||||
c += ['--opt',
|
||||
b"=".join([to_bytes(k, errors='surrogate_or_strict')
|
||||
for k in opt])]
|
||||
if opt[0] == "bridge_name":
|
||||
opt = ("com.docker.network.bridge.name", opt[1])
|
||||
if opt[0] == "driver_mtu":
|
||||
opt = ("com.docker.network.driver.mtu", opt[1])
|
||||
c += [
|
||||
"--opt",
|
||||
b"=".join([to_bytes(k, errors="surrogate_or_strict") for k in opt]),
|
||||
]
|
||||
return c
|
||||
|
||||
def addparam_route(self, c):
|
||||
for route in self.params['route']:
|
||||
c += ['--route', route]
|
||||
for route in self.params["route"]:
|
||||
c += ["--route", route]
|
||||
return c
|
||||
|
||||
def addparam_ipam_driver(self, c):
|
||||
return c + ['--ipam-driver=%s' % self.params['ipam_driver']]
|
||||
return c + ["--ipam-driver=%s" % self.params["ipam_driver"]]
|
||||
|
||||
def addparam_disable_dns(self, c):
|
||||
return c + ['--disable-dns=%s' % self.params['disable_dns']]
|
||||
return c + ["--disable-dns=%s" % self.params["disable_dns"]]
|
||||
|
||||
|
||||
class PodmanNetworkDefaults:
|
||||
|
|
@ -429,8 +443,8 @@ class PodmanNetworkDefaults:
|
|||
self.module = module
|
||||
self.version = podman_version
|
||||
self.defaults = {
|
||||
'driver': 'bridge',
|
||||
'internal': False,
|
||||
"driver": "bridge",
|
||||
"internal": False,
|
||||
}
|
||||
|
||||
def default_dict(self):
|
||||
|
|
@ -445,13 +459,14 @@ class PodmanNetworkDiff:
|
|||
self.default_dict = None
|
||||
self.info = lower_keys(info)
|
||||
self.params = self.defaultize()
|
||||
self.diff = {'before': {}, 'after': {}}
|
||||
self.diff = {"before": {}, "after": {}}
|
||||
self.non_idempotent = {}
|
||||
|
||||
def defaultize(self):
|
||||
params_with_defaults = {}
|
||||
self.default_dict = PodmanNetworkDefaults(
|
||||
self.module, self.version).default_dict()
|
||||
self.module, self.version
|
||||
).default_dict()
|
||||
for p in self.module.params:
|
||||
if self.module.params[p] is None and p in self.default_dict:
|
||||
params_with_defaults[p] = self.default_dict[p]
|
||||
|
|
@ -461,171 +476,189 @@ class PodmanNetworkDiff:
|
|||
|
||||
def _diff_update_and_compare(self, param_name, before, after):
|
||||
if before != after:
|
||||
self.diff['before'].update({param_name: before})
|
||||
self.diff['after'].update({param_name: after})
|
||||
self.diff["before"].update({param_name: before})
|
||||
self.diff["after"].update({param_name: after})
|
||||
return True
|
||||
return False
|
||||
|
||||
def diffparam_disable_dns(self):
|
||||
# For v3 it's impossible to find out DNS settings.
|
||||
if LooseVersion(self.version) >= LooseVersion('4.0.0'):
|
||||
before = not self.info.get('dns_enabled', True)
|
||||
after = self.params['disable_dns']
|
||||
if LooseVersion(self.version) >= LooseVersion("4.0.0"):
|
||||
before = not self.info.get("dns_enabled", True)
|
||||
after = self.params["disable_dns"]
|
||||
# compare only if set explicitly
|
||||
if self.params['disable_dns'] is None:
|
||||
if self.params["disable_dns"] is None:
|
||||
after = before
|
||||
return self._diff_update_and_compare('disable_dns', before, after)
|
||||
before = after = self.params['disable_dns']
|
||||
return self._diff_update_and_compare('disable_dns', before, after)
|
||||
return self._diff_update_and_compare("disable_dns", before, after)
|
||||
before = after = self.params["disable_dns"]
|
||||
return self._diff_update_and_compare("disable_dns", before, after)
|
||||
|
||||
def diffparam_dns(self):
|
||||
before = self.info.get('network_dns_servers', [])
|
||||
after = self.params['dns'] or []
|
||||
return self._diff_update_and_compare('dns', sorted(before), sorted(after))
|
||||
before = self.info.get("network_dns_servers", [])
|
||||
after = self.params["dns"] or []
|
||||
return self._diff_update_and_compare("dns", sorted(before), sorted(after))
|
||||
|
||||
def diffparam_driver(self):
|
||||
# Currently only bridge is supported
|
||||
before = after = 'bridge'
|
||||
return self._diff_update_and_compare('driver', before, after)
|
||||
before = after = "bridge"
|
||||
return self._diff_update_and_compare("driver", before, after)
|
||||
|
||||
def diffparam_ipv6(self):
|
||||
# We don't support dual stack because it generates subnets randomly
|
||||
return self._diff_update_and_compare('ipv6', '', '')
|
||||
return self._diff_update_and_compare("ipv6", "", "")
|
||||
|
||||
def diffparam_gateway(self):
|
||||
# Disable idempotency of subnet for v4, subnets are added automatically
|
||||
# TODO(sshnaidm): check if it's still the issue in v5
|
||||
if LooseVersion(self.version) < LooseVersion('4.0.0'):
|
||||
if LooseVersion(self.version) < LooseVersion("4.0.0"):
|
||||
try:
|
||||
before = self.info['plugins'][0]['ipam']['ranges'][0][0]['gateway']
|
||||
before = self.info["plugins"][0]["ipam"]["ranges"][0][0]["gateway"]
|
||||
except (IndexError, KeyError):
|
||||
before = ''
|
||||
before = ""
|
||||
after = before
|
||||
if self.params['gateway'] is not None:
|
||||
after = self.params['gateway']
|
||||
return self._diff_update_and_compare('gateway', before, after)
|
||||
if self.params["gateway"] is not None:
|
||||
after = self.params["gateway"]
|
||||
return self._diff_update_and_compare("gateway", before, after)
|
||||
else:
|
||||
before_subs = self.info.get('subnets')
|
||||
after = self.params['gateway']
|
||||
before_subs = self.info.get("subnets")
|
||||
after = self.params["gateway"]
|
||||
if not before_subs:
|
||||
before = None
|
||||
if before_subs:
|
||||
if len(before_subs) > 1 and after:
|
||||
return self._diff_update_and_compare(
|
||||
'gateway', ",".join([i['gateway'] for i in before_subs]), after)
|
||||
before = [i.get('gateway') for i in before_subs][0]
|
||||
"gateway", ",".join([i["gateway"] for i in before_subs]), after
|
||||
)
|
||||
before = [i.get("gateway") for i in before_subs][0]
|
||||
if not after:
|
||||
after = before
|
||||
return self._diff_update_and_compare('gateway', before, after)
|
||||
return self._diff_update_and_compare("gateway", before, after)
|
||||
|
||||
def diffparam_internal(self):
|
||||
if LooseVersion(self.version) >= LooseVersion('4.0.0'):
|
||||
before = self.info.get('internal', False)
|
||||
after = self.params['internal']
|
||||
return self._diff_update_and_compare('internal', before, after)
|
||||
if LooseVersion(self.version) >= LooseVersion("4.0.0"):
|
||||
before = self.info.get("internal", False)
|
||||
after = self.params["internal"]
|
||||
return self._diff_update_and_compare("internal", before, after)
|
||||
try:
|
||||
before = not self.info['plugins'][0]['isgateway']
|
||||
before = not self.info["plugins"][0]["isgateway"]
|
||||
except (IndexError, KeyError):
|
||||
before = False
|
||||
after = self.params['internal']
|
||||
return self._diff_update_and_compare('internal', before, after)
|
||||
after = self.params["internal"]
|
||||
return self._diff_update_and_compare("internal", before, after)
|
||||
|
||||
def diffparam_ip_range(self):
|
||||
# TODO(sshnaidm): implement IP to CIDR convert and vice versa
|
||||
before = after = ''
|
||||
return self._diff_update_and_compare('ip_range', before, after)
|
||||
before = after = ""
|
||||
return self._diff_update_and_compare("ip_range", before, after)
|
||||
|
||||
def diffparam_ipam_driver(self):
|
||||
before = self.info.get("ipam_options", {}).get("driver", "")
|
||||
after = self.params['ipam_driver']
|
||||
after = self.params["ipam_driver"]
|
||||
if not after:
|
||||
after = before
|
||||
return self._diff_update_and_compare('ipam_driver', before, after)
|
||||
return self._diff_update_and_compare("ipam_driver", before, after)
|
||||
|
||||
def diffparam_net_config(self):
|
||||
after = self.params['net_config']
|
||||
after = self.params["net_config"]
|
||||
if not after:
|
||||
return self._diff_update_and_compare('net_config', '', '')
|
||||
before_subs = self.info.get('subnets', [])
|
||||
return self._diff_update_and_compare("net_config", "", "")
|
||||
before_subs = self.info.get("subnets", [])
|
||||
if before_subs:
|
||||
before = ":".join(sorted([",".join([i['subnet'], i['gateway']]).rstrip(",") for i in before_subs]))
|
||||
before = ":".join(
|
||||
sorted(
|
||||
[
|
||||
",".join([i["subnet"], i["gateway"]]).rstrip(",")
|
||||
for i in before_subs
|
||||
]
|
||||
)
|
||||
)
|
||||
else:
|
||||
before = ''
|
||||
after = ":".join(sorted([",".join([i['subnet'], i['gateway']]).rstrip(",") for i in after]))
|
||||
return self._diff_update_and_compare('net_config', before, after)
|
||||
before = ""
|
||||
after = ":".join(
|
||||
sorted([",".join([i["subnet"], i["gateway"]]).rstrip(",") for i in after])
|
||||
)
|
||||
return self._diff_update_and_compare("net_config", before, after)
|
||||
|
||||
def diffparam_route(self):
|
||||
routes = self.info.get('routes', [])
|
||||
routes = self.info.get("routes", [])
|
||||
if routes:
|
||||
before = [",".join([
|
||||
r['destination'], r['gateway'], str(r.get('metric', ''))]).rstrip(",") for r in routes]
|
||||
before = [
|
||||
",".join(
|
||||
[r["destination"], r["gateway"], str(r.get("metric", ""))]
|
||||
).rstrip(",")
|
||||
for r in routes
|
||||
]
|
||||
else:
|
||||
before = []
|
||||
after = self.params['route'] or []
|
||||
return self._diff_update_and_compare('route', sorted(before), sorted(after))
|
||||
after = self.params["route"] or []
|
||||
return self._diff_update_and_compare("route", sorted(before), sorted(after))
|
||||
|
||||
def diffparam_subnet(self):
|
||||
# Disable idempotency of subnet for v3 and below
|
||||
if LooseVersion(self.version) < LooseVersion('4.0.0'):
|
||||
if LooseVersion(self.version) < LooseVersion("4.0.0"):
|
||||
try:
|
||||
before = self.info['plugins'][0]['ipam']['ranges'][0][0]['subnet']
|
||||
before = self.info["plugins"][0]["ipam"]["ranges"][0][0]["subnet"]
|
||||
except (IndexError, KeyError):
|
||||
before = ''
|
||||
before = ""
|
||||
after = before
|
||||
if self.params['subnet'] is not None:
|
||||
after = self.params['subnet']
|
||||
if self.params["subnet"] is not None:
|
||||
after = self.params["subnet"]
|
||||
if HAS_IP_ADDRESS_MODULE:
|
||||
after = ipaddress.ip_network(after).compressed
|
||||
return self._diff_update_and_compare('subnet', before, after)
|
||||
return self._diff_update_and_compare("subnet", before, after)
|
||||
else:
|
||||
if self.params['ipv6'] is not None:
|
||||
if self.params["ipv6"] is not None:
|
||||
# We can't support dual stack, it generates subnets randomly
|
||||
return self._diff_update_and_compare('subnet', '', '')
|
||||
after = self.params['subnet']
|
||||
return self._diff_update_and_compare("subnet", "", "")
|
||||
after = self.params["subnet"]
|
||||
if after is None:
|
||||
# We can't guess what subnet was used before by default
|
||||
return self._diff_update_and_compare('subnet', '', '')
|
||||
before = self.info.get('subnets')
|
||||
return self._diff_update_and_compare("subnet", "", "")
|
||||
before = self.info.get("subnets")
|
||||
if before:
|
||||
if len(before) > 1 and after:
|
||||
return self._diff_update_and_compare('subnet', ",".join([i['subnet'] for i in before]), after)
|
||||
before = [i['subnet'] for i in before][0]
|
||||
return self._diff_update_and_compare('subnet', before, after)
|
||||
return self._diff_update_and_compare(
|
||||
"subnet", ",".join([i["subnet"] for i in before]), after
|
||||
)
|
||||
before = [i["subnet"] for i in before][0]
|
||||
return self._diff_update_and_compare("subnet", before, after)
|
||||
|
||||
def diffparam_macvlan(self):
|
||||
before = after = ''
|
||||
return self._diff_update_and_compare('macvlan', before, after)
|
||||
before = after = ""
|
||||
return self._diff_update_and_compare("macvlan", before, after)
|
||||
|
||||
def diffparam_opt(self):
|
||||
if LooseVersion(self.version) >= LooseVersion('4.0.0'):
|
||||
vlan_before = self.info.get('options', {}).get('vlan')
|
||||
if LooseVersion(self.version) >= LooseVersion("4.0.0"):
|
||||
vlan_before = self.info.get("options", {}).get("vlan")
|
||||
else:
|
||||
try:
|
||||
vlan_before = self.info['plugins'][0].get('vlan')
|
||||
vlan_before = self.info["plugins"][0].get("vlan")
|
||||
except (IndexError, KeyError):
|
||||
vlan_before = None
|
||||
vlan_after = self.params['opt'].get('vlan') if self.params['opt'] else None
|
||||
vlan_after = self.params["opt"].get("vlan") if self.params["opt"] else None
|
||||
if vlan_before or vlan_after:
|
||||
before, after = {'vlan': str(vlan_before)}, {'vlan': str(vlan_after)}
|
||||
before, after = {"vlan": str(vlan_before)}, {"vlan": str(vlan_after)}
|
||||
else:
|
||||
before, after = {}, {}
|
||||
if LooseVersion(self.version) >= LooseVersion('4.0.0'):
|
||||
mtu_before = self.info.get('options', {}).get('mtu')
|
||||
if LooseVersion(self.version) >= LooseVersion("4.0.0"):
|
||||
mtu_before = self.info.get("options", {}).get("mtu")
|
||||
else:
|
||||
try:
|
||||
mtu_before = self.info['plugins'][0].get('mtu')
|
||||
mtu_before = self.info["plugins"][0].get("mtu")
|
||||
except (IndexError, KeyError):
|
||||
mtu_before = None
|
||||
mtu_after = self.params['opt'].get('mtu') if self.params['opt'] else None
|
||||
mtu_after = self.params["opt"].get("mtu") if self.params["opt"] else None
|
||||
if mtu_before or mtu_after:
|
||||
before.update({'mtu': str(mtu_before)})
|
||||
after.update({'mtu': str(mtu_after)})
|
||||
return self._diff_update_and_compare('opt', before, after)
|
||||
before.update({"mtu": str(mtu_before)})
|
||||
after.update({"mtu": str(mtu_after)})
|
||||
return self._diff_update_and_compare("opt", before, after)
|
||||
|
||||
def is_different(self):
|
||||
diff_func_list = [func for func in dir(self)
|
||||
if callable(getattr(self, func)) and func.startswith(
|
||||
"diffparam")]
|
||||
diff_func_list = [
|
||||
func
|
||||
for func in dir(self)
|
||||
if callable(getattr(self, func)) and func.startswith("diffparam")
|
||||
]
|
||||
fail_fast = not bool(self.module._diff)
|
||||
different = False
|
||||
for func_name in diff_func_list:
|
||||
|
|
@ -636,7 +669,11 @@ class PodmanNetworkDiff:
|
|||
different = True
|
||||
# Check non idempotent parameters
|
||||
for p in self.non_idempotent:
|
||||
if self.module.params[p] is not None and self.module.params[p] not in [{}, [], '']:
|
||||
if self.module.params[p] is not None and self.module.params[p] not in [
|
||||
{},
|
||||
[],
|
||||
"",
|
||||
]:
|
||||
different = True
|
||||
return different
|
||||
|
||||
|
|
@ -658,7 +695,7 @@ class PodmanNetwork:
|
|||
super(PodmanNetwork, self).__init__()
|
||||
self.module = module
|
||||
self.name = name
|
||||
self.stdout, self.stderr = '', ''
|
||||
self.stdout, self.stderr = "", ""
|
||||
self.info = self.get_info()
|
||||
self.version = self._get_podman_version()
|
||||
self.diff = {}
|
||||
|
|
@ -672,35 +709,41 @@ class PodmanNetwork:
|
|||
@property
|
||||
def different(self):
|
||||
"""Check if network is different."""
|
||||
diffcheck = PodmanNetworkDiff(
|
||||
self.module,
|
||||
self.info,
|
||||
self.version)
|
||||
diffcheck = PodmanNetworkDiff(self.module, self.info, self.version)
|
||||
is_different = diffcheck.is_different()
|
||||
diffs = diffcheck.diff
|
||||
if self.module._diff and is_different and diffs['before'] and diffs['after']:
|
||||
self.diff['before'] = "\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(
|
||||
diffs['before'].items())]) + "\n"
|
||||
self.diff['after'] = "\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(
|
||||
diffs['after'].items())]) + "\n"
|
||||
if self.module._diff and is_different and diffs["before"] and diffs["after"]:
|
||||
self.diff["before"] = (
|
||||
"\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(diffs["before"].items())]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
self.diff["after"] = (
|
||||
"\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(diffs["after"].items())]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
return is_different
|
||||
|
||||
def get_info(self):
|
||||
"""Inspect network and gather info about it."""
|
||||
# pylint: disable=unused-variable
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'network', b'inspect', self.name])
|
||||
[self.module.params["executable"], b"network", b"inspect", self.name]
|
||||
)
|
||||
return json.loads(out)[0] if rc == 0 else {}
|
||||
|
||||
def _get_podman_version(self):
|
||||
# pylint: disable=unused-variable
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'--version'])
|
||||
[self.module.params["executable"], b"--version"]
|
||||
)
|
||||
if rc != 0 or not out or "version" not in out:
|
||||
self.module.fail_json(msg="%s run failed!" %
|
||||
self.module.params['executable'])
|
||||
self.module.fail_json(
|
||||
msg="%s run failed!" % self.module.params["executable"]
|
||||
)
|
||||
return out.split("version")[1].strip()
|
||||
|
||||
def _perform_action(self, action):
|
||||
|
|
@ -709,33 +752,39 @@ class PodmanNetwork:
|
|||
Arguments:
|
||||
action {str} -- action to perform - create, stop, delete
|
||||
"""
|
||||
b_command = PodmanNetworkModuleParams(action,
|
||||
self.module.params,
|
||||
self.version,
|
||||
self.module,
|
||||
).construct_command_from_params()
|
||||
full_cmd = " ".join([self.module.params['executable'], 'network']
|
||||
+ [to_native(i) for i in b_command])
|
||||
b_command = PodmanNetworkModuleParams(
|
||||
action,
|
||||
self.module.params,
|
||||
self.version,
|
||||
self.module,
|
||||
).construct_command_from_params()
|
||||
full_cmd = " ".join(
|
||||
[self.module.params["executable"], "network"]
|
||||
+ [to_native(i) for i in b_command]
|
||||
)
|
||||
self.module.log("PODMAN-NETWORK-DEBUG: %s" % full_cmd)
|
||||
self.actions.append(full_cmd)
|
||||
if not self.module.check_mode:
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'network'] + b_command,
|
||||
expand_user_and_vars=False)
|
||||
[self.module.params["executable"], b"network"] + b_command,
|
||||
expand_user_and_vars=False,
|
||||
)
|
||||
self.stdout = out
|
||||
self.stderr = err
|
||||
if rc != 0:
|
||||
self.module.fail_json(
|
||||
msg="Can't %s network %s" % (action, self.name),
|
||||
stdout=out, stderr=err)
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
)
|
||||
|
||||
def delete(self):
|
||||
"""Delete the network."""
|
||||
self._perform_action('delete')
|
||||
self._perform_action("delete")
|
||||
|
||||
def create(self):
|
||||
"""Create the network."""
|
||||
self._perform_action('create')
|
||||
self._perform_action("create")
|
||||
|
||||
def recreate(self):
|
||||
"""Recreate the network."""
|
||||
|
|
@ -760,16 +809,16 @@ class PodmanNetworkManager:
|
|||
|
||||
self.module = module
|
||||
self.results = {
|
||||
'changed': False,
|
||||
'actions': [],
|
||||
'network': {},
|
||||
"changed": False,
|
||||
"actions": [],
|
||||
"network": {},
|
||||
}
|
||||
self.name = self.module.params['name']
|
||||
self.executable = \
|
||||
self.module.get_bin_path(self.module.params['executable'],
|
||||
required=True)
|
||||
self.state = self.module.params['state']
|
||||
self.recreate = self.module.params['recreate']
|
||||
self.name = self.module.params["name"]
|
||||
self.executable = self.module.get_bin_path(
|
||||
self.module.params["executable"], required=True
|
||||
)
|
||||
self.state = self.module.params["state"]
|
||||
self.recreate = self.module.params["recreate"]
|
||||
self.network = PodmanNetwork(self.module, self.name)
|
||||
|
||||
def update_network_result(self, changed=True):
|
||||
|
|
@ -781,37 +830,43 @@ class PodmanNetworkManager:
|
|||
"""
|
||||
facts = self.network.get_info() if changed else self.network.info
|
||||
out, err = self.network.stdout, self.network.stderr
|
||||
self.results.update({'changed': changed, 'network': facts,
|
||||
'podman_actions': self.network.actions},
|
||||
stdout=out, stderr=err)
|
||||
self.results.update(
|
||||
{
|
||||
"changed": changed,
|
||||
"network": facts,
|
||||
"podman_actions": self.network.actions,
|
||||
},
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
)
|
||||
if self.network.diff:
|
||||
self.results.update({'diff': self.network.diff})
|
||||
if self.module.params['debug']:
|
||||
self.results.update({'podman_version': self.network.version})
|
||||
self.results.update({"diff": self.network.diff})
|
||||
if self.module.params["debug"]:
|
||||
self.results.update({"podman_version": self.network.version})
|
||||
self.module.exit_json(**self.results)
|
||||
|
||||
def execute(self):
|
||||
"""Execute the desired action according to map of actions & states."""
|
||||
states_map = {
|
||||
'present': self.make_present,
|
||||
'absent': self.make_absent,
|
||||
'quadlet': self.make_quadlet,
|
||||
"present": self.make_present,
|
||||
"absent": self.make_absent,
|
||||
"quadlet": self.make_quadlet,
|
||||
}
|
||||
process_action = states_map[self.state]
|
||||
process_action()
|
||||
self.module.fail_json(msg="Unexpected logic error happened, "
|
||||
"please contact maintainers ASAP!")
|
||||
self.module.fail_json(
|
||||
msg="Unexpected logic error happened, " "please contact maintainers ASAP!"
|
||||
)
|
||||
|
||||
def make_present(self):
|
||||
"""Run actions if desired state is 'started'."""
|
||||
if not self.network.exists:
|
||||
self.network.create()
|
||||
self.results['actions'].append('created %s' % self.network.name)
|
||||
self.results["actions"].append("created %s" % self.network.name)
|
||||
self.update_network_result()
|
||||
elif self.recreate or self.network.different:
|
||||
self.network.recreate()
|
||||
self.results['actions'].append('recreated %s' %
|
||||
self.network.name)
|
||||
self.results["actions"].append("recreated %s" % self.network.name)
|
||||
self.update_network_result()
|
||||
else:
|
||||
self.update_network_result(changed=False)
|
||||
|
|
@ -819,13 +874,12 @@ class PodmanNetworkManager:
|
|||
def make_absent(self):
|
||||
"""Run actions if desired state is 'absent'."""
|
||||
if not self.network.exists:
|
||||
self.results.update({'changed': False})
|
||||
self.results.update({"changed": False})
|
||||
elif self.network.exists:
|
||||
self.network.delete()
|
||||
self.results['actions'].append('deleted %s' % self.network.name)
|
||||
self.results.update({'changed': True})
|
||||
self.results.update({'network': {},
|
||||
'podman_actions': self.network.actions})
|
||||
self.results["actions"].append("deleted %s" % self.network.name)
|
||||
self.results.update({"changed": True})
|
||||
self.results.update({"network": {}, "podman_actions": self.network.actions})
|
||||
self.module.exit_json(**self.results)
|
||||
|
||||
def make_quadlet(self):
|
||||
|
|
@ -837,60 +891,70 @@ class PodmanNetworkManager:
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
state=dict(type='str', default="present",
|
||||
choices=['present', 'absent', 'quadlet']),
|
||||
name=dict(type='str', required=True),
|
||||
disable_dns=dict(type='bool', required=False),
|
||||
dns=dict(type='list', elements='str', required=False),
|
||||
driver=dict(type='str', required=False),
|
||||
force=dict(type='bool', default=False),
|
||||
gateway=dict(type='str', required=False),
|
||||
interface_name=dict(type='str', required=False),
|
||||
internal=dict(type='bool', required=False),
|
||||
ip_range=dict(type='str', required=False),
|
||||
ipam_driver=dict(type='str', required=False,
|
||||
choices=['host-local', 'dhcp', 'none']),
|
||||
ipv6=dict(type='bool', required=False),
|
||||
subnet=dict(type='str', required=False),
|
||||
macvlan=dict(type='str', required=False),
|
||||
opt=dict(type='dict', required=False,
|
||||
options=dict(
|
||||
isolate=dict(type='bool', required=False),
|
||||
mtu=dict(type='int', required=False),
|
||||
metric=dict(type='int', required=False),
|
||||
mode=dict(type='str', required=False),
|
||||
parent=dict(type='str', required=False),
|
||||
vlan=dict(type='int', required=False),
|
||||
bclim=dict(type='int', required=False),
|
||||
no_default_route=dict(type='str', required=False),
|
||||
vrf=dict(type='str', required=False),
|
||||
bridge_name=dict(type='str', required=False),
|
||||
driver_mtu=dict(type='str', required=False),
|
||||
)),
|
||||
executable=dict(type='str', required=False, default='podman'),
|
||||
debug=dict(type='bool', default=False),
|
||||
recreate=dict(type='bool', default=False),
|
||||
route=dict(type='list', elements='str', required=False),
|
||||
quadlet_dir=dict(type='path', required=False),
|
||||
quadlet_filename=dict(type='str', required=False),
|
||||
quadlet_file_mode=dict(type='raw', required=False),
|
||||
quadlet_options=dict(type='list', elements='str', required=False),
|
||||
net_config=dict(type='list', required=False, elements='dict',
|
||||
options=dict(
|
||||
subnet=dict(type='str', required=True),
|
||||
gateway=dict(type='str', required=True),
|
||||
ip_range=dict(type='str', required=False),
|
||||
)),
|
||||
state=dict(
|
||||
type="str", default="present", choices=["present", "absent", "quadlet"]
|
||||
),
|
||||
name=dict(type="str", required=True),
|
||||
disable_dns=dict(type="bool", required=False),
|
||||
dns=dict(type="list", elements="str", required=False),
|
||||
driver=dict(type="str", required=False),
|
||||
force=dict(type="bool", default=False),
|
||||
gateway=dict(type="str", required=False),
|
||||
interface_name=dict(type="str", required=False),
|
||||
internal=dict(type="bool", required=False),
|
||||
ip_range=dict(type="str", required=False),
|
||||
ipam_driver=dict(
|
||||
type="str", required=False, choices=["host-local", "dhcp", "none"]
|
||||
),
|
||||
ipv6=dict(type="bool", required=False),
|
||||
subnet=dict(type="str", required=False),
|
||||
macvlan=dict(type="str", required=False),
|
||||
opt=dict(
|
||||
type="dict",
|
||||
required=False,
|
||||
options=dict(
|
||||
isolate=dict(type="bool", required=False),
|
||||
mtu=dict(type="int", required=False),
|
||||
metric=dict(type="int", required=False),
|
||||
mode=dict(type="str", required=False),
|
||||
parent=dict(type="str", required=False),
|
||||
vlan=dict(type="int", required=False),
|
||||
bclim=dict(type="int", required=False),
|
||||
no_default_route=dict(type="str", required=False),
|
||||
vrf=dict(type="str", required=False),
|
||||
bridge_name=dict(type="str", required=False),
|
||||
driver_mtu=dict(type="str", required=False),
|
||||
),
|
||||
),
|
||||
executable=dict(type="str", required=False, default="podman"),
|
||||
debug=dict(type="bool", default=False),
|
||||
recreate=dict(type="bool", default=False),
|
||||
route=dict(type="list", elements="str", required=False),
|
||||
quadlet_dir=dict(type="path", required=False),
|
||||
quadlet_filename=dict(type="str", required=False),
|
||||
quadlet_file_mode=dict(type="raw", required=False),
|
||||
quadlet_options=dict(type="list", elements="str", required=False),
|
||||
net_config=dict(
|
||||
type="list",
|
||||
required=False,
|
||||
elements="dict",
|
||||
options=dict(
|
||||
subnet=dict(type="str", required=True),
|
||||
gateway=dict(type="str", required=True),
|
||||
ip_range=dict(type="str", required=False),
|
||||
),
|
||||
),
|
||||
),
|
||||
required_by=dict( # for IP range and GW to set 'subnet' is required
|
||||
ip_range=('subnet'),
|
||||
gateway=('subnet'),
|
||||
ip_range=("subnet"),
|
||||
gateway=("subnet"),
|
||||
),
|
||||
# define or subnet or net config
|
||||
mutually_exclusive=[['subnet', 'net_config']])
|
||||
mutually_exclusive=[["subnet", "net_config"]],
|
||||
)
|
||||
|
||||
PodmanNetworkManager(module).execute()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
# 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
|
||||
|
||||
|
||||
|
|
@ -82,9 +83,9 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
|
||||
def get_network_info(module, executable, name):
|
||||
command = [executable, 'network', 'inspect']
|
||||
command = [executable, "network", "inspect"]
|
||||
if not name:
|
||||
all_names = [executable, 'network', 'ls', '-q']
|
||||
all_names = [executable, "network", "ls", "-q"]
|
||||
rc, out, err = module.run_command(all_names)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Unable to get list of networks: %s" % err)
|
||||
|
|
@ -95,7 +96,7 @@ def get_network_info(module, executable, name):
|
|||
else:
|
||||
command.append(name)
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0 or 'unable to find network configuration' in err:
|
||||
if rc != 0 or "unable to find network configuration" in err:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (name, err))
|
||||
if not out or json.loads(out) is None:
|
||||
return [], out, err
|
||||
|
|
@ -105,25 +106,20 @@ def get_network_info(module, executable, name):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
name=dict(type='str')
|
||||
executable=dict(type="str", default="podman"), name=dict(type="str")
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
name = module.params["name"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
inspect_results, out, err = get_network_info(module, executable, name)
|
||||
|
||||
results = {
|
||||
"changed": False,
|
||||
"networks": inspect_results,
|
||||
"stderr": err
|
||||
}
|
||||
results = {"changed": False, "networks": inspect_results, "stderr": err}
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_play
|
||||
author:
|
||||
- "Sagi Shnaidman (@sshnaidm)"
|
||||
|
|
@ -195,9 +195,9 @@ options:
|
|||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
- name: Play kube file
|
||||
containers.podman.podman_play:
|
||||
kube_file: ~/kube.yaml
|
||||
|
|
@ -229,17 +229,24 @@ EXAMPLES = '''
|
|||
quadlet_options:
|
||||
- "SetWorkingDirectory=yaml"
|
||||
- "ExitCodePropagation=any"
|
||||
'''
|
||||
"""
|
||||
import re # noqa: F402
|
||||
|
||||
try:
|
||||
import yaml
|
||||
|
||||
HAS_YAML = True
|
||||
except ImportError:
|
||||
HAS_YAML = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: F402
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion, get_podman_version
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state # noqa: F402
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
LooseVersion,
|
||||
get_podman_version,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import (
|
||||
create_quadlet_state,
|
||||
) # noqa: F402
|
||||
|
||||
|
||||
class PodmanKubeManagement:
|
||||
|
|
@ -248,115 +255,127 @@ class PodmanKubeManagement:
|
|||
self.module = module
|
||||
self.actions = []
|
||||
self.executable = executable
|
||||
self.command = [self.executable, 'play', 'kube']
|
||||
self.command = [self.executable, "play", "kube"]
|
||||
self.version = get_podman_version(module)
|
||||
creds = []
|
||||
# pod_name = extract_pod_name(module.params['kube_file'])
|
||||
if self.module.params['annotation']:
|
||||
for k, v in self.module.params['annotation'].items():
|
||||
self.command.extend(['--annotation', '{k}={v}'.format(k=k, v=v)])
|
||||
if self.module.params['username']:
|
||||
creds += [self.module.params['username']]
|
||||
if self.module.params['password']:
|
||||
creds += [self.module.params['password']]
|
||||
if self.module.params["annotation"]:
|
||||
for k, v in self.module.params["annotation"].items():
|
||||
self.command.extend(["--annotation", "{k}={v}".format(k=k, v=v)])
|
||||
if self.module.params["username"]:
|
||||
creds += [self.module.params["username"]]
|
||||
if self.module.params["password"]:
|
||||
creds += [self.module.params["password"]]
|
||||
creds = ":".join(creds)
|
||||
self.command.extend(['--creds=%s' % creds])
|
||||
if self.module.params['network']:
|
||||
networks = ",".join(self.module.params['network'])
|
||||
self.command.extend(['--network=%s' % networks])
|
||||
if self.module.params['configmap']:
|
||||
configmaps = ",".join(self.module.params['configmap'])
|
||||
self.command.extend(['--configmap=%s' % configmaps])
|
||||
if self.module.params['log_opt']:
|
||||
for k, v in self.module.params['log_opt'].items():
|
||||
self.command.extend(['--log-opt', '{k}={v}'.format(k=k.replace('_', '-'), v=v)])
|
||||
start = self.module.params['state'] == 'started'
|
||||
self.command.extend(['--start=%s' % str(start).lower()])
|
||||
self.command.extend(["--creds=%s" % creds])
|
||||
if self.module.params["network"]:
|
||||
networks = ",".join(self.module.params["network"])
|
||||
self.command.extend(["--network=%s" % networks])
|
||||
if self.module.params["configmap"]:
|
||||
configmaps = ",".join(self.module.params["configmap"])
|
||||
self.command.extend(["--configmap=%s" % configmaps])
|
||||
if self.module.params["log_opt"]:
|
||||
for k, v in self.module.params["log_opt"].items():
|
||||
self.command.extend(
|
||||
["--log-opt", "{k}={v}".format(k=k.replace("_", "-"), v=v)]
|
||||
)
|
||||
start = self.module.params["state"] == "started"
|
||||
self.command.extend(["--start=%s" % str(start).lower()])
|
||||
for arg, param in {
|
||||
'--authfile': 'authfile',
|
||||
'--build': 'build',
|
||||
'--cert-dir': 'cert_dir',
|
||||
'--context-dir': 'context_dir',
|
||||
'--log-driver': 'log_driver',
|
||||
'--seccomp-profile-root': 'seccomp_profile_root',
|
||||
'--tls-verify': 'tls_verify',
|
||||
'--log-level': 'log_level',
|
||||
'--userns': 'userns',
|
||||
'--quiet': 'quiet',
|
||||
"--authfile": "authfile",
|
||||
"--build": "build",
|
||||
"--cert-dir": "cert_dir",
|
||||
"--context-dir": "context_dir",
|
||||
"--log-driver": "log_driver",
|
||||
"--seccomp-profile-root": "seccomp_profile_root",
|
||||
"--tls-verify": "tls_verify",
|
||||
"--log-level": "log_level",
|
||||
"--userns": "userns",
|
||||
"--quiet": "quiet",
|
||||
}.items():
|
||||
if self.module.params[param] is not None:
|
||||
self.command += ["%s=%s" % (arg, self.module.params[param])]
|
||||
if self.module.params['kube_file']:
|
||||
self.command += [self.module.params['kube_file']]
|
||||
elif self.module.params['kube_file_content']:
|
||||
self.command += ['-']
|
||||
if self.module.params["kube_file"]:
|
||||
self.command += [self.module.params["kube_file"]]
|
||||
elif self.module.params["kube_file_content"]:
|
||||
self.command += ["-"]
|
||||
|
||||
def _command_run(self, cmd):
|
||||
if self.module.params['kube_file_content']:
|
||||
rc, out, err = self.module.run_command(cmd, data=self.module.params['kube_file_content'])
|
||||
if self.module.params["kube_file_content"]:
|
||||
rc, out, err = self.module.run_command(
|
||||
cmd, data=self.module.params["kube_file_content"]
|
||||
)
|
||||
else:
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
self.actions.append(" ".join(cmd))
|
||||
if self.module.params['debug']:
|
||||
self.module.log('PODMAN-PLAY-KUBE command: %s' % " ".join(cmd))
|
||||
self.module.log('PODMAN-PLAY-KUBE stdout: %s' % out)
|
||||
self.module.log('PODMAN-PLAY-KUBE stderr: %s' % err)
|
||||
self.module.log('PODMAN-PLAY-KUBE rc: %s' % rc)
|
||||
if self.module.params["debug"]:
|
||||
self.module.log("PODMAN-PLAY-KUBE command: %s" % " ".join(cmd))
|
||||
self.module.log("PODMAN-PLAY-KUBE stdout: %s" % out)
|
||||
self.module.log("PODMAN-PLAY-KUBE stderr: %s" % err)
|
||||
self.module.log("PODMAN-PLAY-KUBE rc: %s" % rc)
|
||||
return rc, out, err
|
||||
|
||||
def tear_down_pods(self):
|
||||
'''
|
||||
"""
|
||||
Tear down the pod and contaiers by using --down option in kube play
|
||||
which is supported since Podman 3.4.0
|
||||
'''
|
||||
"""
|
||||
changed = False
|
||||
kube_file = self.module.params['kube_file']
|
||||
kube_file_content = self.module.params['kube_file_content']
|
||||
kube_file = self.module.params["kube_file"]
|
||||
kube_file_content = self.module.params["kube_file_content"]
|
||||
if kube_file:
|
||||
rc, out, err = self._command_run([self.executable, "kube", "play", "--down", kube_file])
|
||||
rc, out, err = self._command_run(
|
||||
[self.executable, "kube", "play", "--down", kube_file]
|
||||
)
|
||||
elif kube_file_content:
|
||||
rc, out, err = self._command_run([self.executable, "kube", "play", "--down", "-"])
|
||||
rc, out, err = self._command_run(
|
||||
[self.executable, "kube", "play", "--down", "-"]
|
||||
)
|
||||
if rc != 0 and "no such pod" in err:
|
||||
changed = False
|
||||
return changed, out, err
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg="Failed to delete Pod with %s: %s %s" % (
|
||||
kube_file if kube_file else "YAML content", out, err))
|
||||
self.module.fail_json(
|
||||
msg="Failed to delete Pod with %s: %s %s"
|
||||
% (kube_file if kube_file else "YAML content", out, err)
|
||||
)
|
||||
|
||||
# hack to check if no resources are deleted
|
||||
for line in out.splitlines():
|
||||
if line and not line.endswith(':'):
|
||||
if line and not line.endswith(":"):
|
||||
changed = True
|
||||
break
|
||||
return changed, out, err
|
||||
|
||||
def discover_pods(self):
|
||||
pod_name = ''
|
||||
if self.module.params['kube_file']:
|
||||
pod_name = ""
|
||||
if self.module.params["kube_file"]:
|
||||
if HAS_YAML:
|
||||
with open(self.module.params['kube_file']) as f:
|
||||
with open(self.module.params["kube_file"]) as f:
|
||||
pods = list(yaml.safe_load_all(f))
|
||||
for pod in pods:
|
||||
if 'metadata' in pod and pod['kind'] in ['Deployment', 'Pod']:
|
||||
pod_name = pod['metadata'].get('name')
|
||||
if "metadata" in pod and pod["kind"] in ["Deployment", "Pod"]:
|
||||
pod_name = pod["metadata"].get("name")
|
||||
else:
|
||||
with open(self.module.params['kube_file']) as text:
|
||||
with open(self.module.params["kube_file"]) as text:
|
||||
# the following formats are matched for a kube name:
|
||||
# should match name field within metadata (2 or 4 spaces in front of name)
|
||||
# the name can be written without quotes, in single or double quotes
|
||||
# the name can contain -_
|
||||
re_pod_name = re.compile(r'^\s{2,4}name: ["|\']?(?P<pod_name>[\w|\-|\_]+)["|\']?', re.MULTILINE)
|
||||
re_pod_name = re.compile(
|
||||
r'^\s{2,4}name: ["|\']?(?P<pod_name>[\w|\-|\_]+)["|\']?',
|
||||
re.MULTILINE,
|
||||
)
|
||||
re_pod = re_pod_name.search(text.read())
|
||||
if re_pod:
|
||||
pod_name = re_pod.group(1)
|
||||
if not pod_name:
|
||||
self.module.fail_json("This Kube file doesn't have Pod or Deployment!")
|
||||
# Find all pods
|
||||
all_pods = ''
|
||||
all_pods = ""
|
||||
# In case of one pod or replicasets
|
||||
for name in ("name=%s$", "name=%s-pod-*"):
|
||||
cmd = [self.executable,
|
||||
"pod", "ps", "-q", "--filter", name % pod_name]
|
||||
cmd = [self.executable, "pod", "ps", "-q", "--filter", name % pod_name]
|
||||
rc, out, err = self._command_run(cmd)
|
||||
all_pods += out
|
||||
ids = list(set([i for i in all_pods.splitlines() if i]))
|
||||
|
|
@ -364,11 +383,12 @@ class PodmanKubeManagement:
|
|||
|
||||
def remove_associated_pods(self, pods):
|
||||
changed = False
|
||||
out_all, err_all = '', ''
|
||||
out_all, err_all = "", ""
|
||||
# Delete all pods
|
||||
for pod_id in pods:
|
||||
rc, out, err = self._command_run(
|
||||
[self.executable, "pod", "rm", "-f", pod_id])
|
||||
[self.executable, "pod", "rm", "-f", pod_id]
|
||||
)
|
||||
if rc != 0:
|
||||
self.module.fail_json("Can NOT delete Pod %s" % pod_id)
|
||||
else:
|
||||
|
|
@ -378,7 +398,9 @@ class PodmanKubeManagement:
|
|||
return changed, out_all, err_all
|
||||
|
||||
def pod_recreate(self):
|
||||
if self.version is not None and LooseVersion(self.version) >= LooseVersion('3.4.0'):
|
||||
if self.version is not None and LooseVersion(self.version) >= LooseVersion(
|
||||
"3.4.0"
|
||||
):
|
||||
self.tear_down_pods()
|
||||
else:
|
||||
pods = self.discover_pods()
|
||||
|
|
@ -392,14 +414,15 @@ class PodmanKubeManagement:
|
|||
|
||||
def play(self):
|
||||
rc, out, err = self._command_run(self.command)
|
||||
if rc != 0 and 'pod already exists' in err:
|
||||
if self.module.params['recreate']:
|
||||
if rc != 0 and "pod already exists" in err:
|
||||
if self.module.params["recreate"]:
|
||||
out, err = self.pod_recreate()
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
err = "\n".join([
|
||||
i for i in err.splitlines() if 'pod already exists' not in i])
|
||||
err = "\n".join(
|
||||
[i for i in err.splitlines() if "pod already exists" not in i]
|
||||
)
|
||||
elif rc != 0:
|
||||
self.module.fail_json(msg="Output: %s\nError=%s" % (out, err))
|
||||
else:
|
||||
|
|
@ -416,63 +439,73 @@ class PodmanKubeManagement:
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
annotation=dict(type='dict', aliases=['annotations']),
|
||||
executable=dict(type='str', default='podman'),
|
||||
kube_file=dict(type='path'),
|
||||
kube_file_content=dict(type='str'),
|
||||
authfile=dict(type='path'),
|
||||
build=dict(type='bool'),
|
||||
cert_dir=dict(type='path'),
|
||||
configmap=dict(type='list', elements='path'),
|
||||
context_dir=dict(type='path'),
|
||||
seccomp_profile_root=dict(type='path'),
|
||||
username=dict(type='str'),
|
||||
password=dict(type='str', no_log=True),
|
||||
log_driver=dict(type='str'),
|
||||
log_opt=dict(type='dict', aliases=['log_options'], options=dict(
|
||||
path=dict(type='str'),
|
||||
max_size=dict(type='str'),
|
||||
tag=dict(type='str'))),
|
||||
network=dict(type='list', elements='str'),
|
||||
annotation=dict(type="dict", aliases=["annotations"]),
|
||||
executable=dict(type="str", default="podman"),
|
||||
kube_file=dict(type="path"),
|
||||
kube_file_content=dict(type="str"),
|
||||
authfile=dict(type="path"),
|
||||
build=dict(type="bool"),
|
||||
cert_dir=dict(type="path"),
|
||||
configmap=dict(type="list", elements="path"),
|
||||
context_dir=dict(type="path"),
|
||||
seccomp_profile_root=dict(type="path"),
|
||||
username=dict(type="str"),
|
||||
password=dict(type="str", no_log=True),
|
||||
log_driver=dict(type="str"),
|
||||
log_opt=dict(
|
||||
type="dict",
|
||||
aliases=["log_options"],
|
||||
options=dict(
|
||||
path=dict(type="str"),
|
||||
max_size=dict(type="str"),
|
||||
tag=dict(type="str"),
|
||||
),
|
||||
),
|
||||
network=dict(type="list", elements="str"),
|
||||
state=dict(
|
||||
type='str',
|
||||
choices=['started', 'created', 'absent', 'quadlet'],
|
||||
required=True),
|
||||
tls_verify=dict(type='bool'),
|
||||
debug=dict(type='bool'),
|
||||
quiet=dict(type='bool'),
|
||||
recreate=dict(type='bool'),
|
||||
userns=dict(type='str'),
|
||||
type="str",
|
||||
choices=["started", "created", "absent", "quadlet"],
|
||||
required=True,
|
||||
),
|
||||
tls_verify=dict(type="bool"),
|
||||
debug=dict(type="bool"),
|
||||
quiet=dict(type="bool"),
|
||||
recreate=dict(type="bool"),
|
||||
userns=dict(type="str"),
|
||||
log_level=dict(
|
||||
type='str',
|
||||
choices=["debug", "info", "warn", "error", "fatal", "panic"]),
|
||||
quadlet_dir=dict(type='path', required=False),
|
||||
quadlet_filename=dict(type='str', required=False),
|
||||
quadlet_file_mode=dict(type='raw', required=False),
|
||||
quadlet_options=dict(type='list', elements='str', required=False),
|
||||
type="str", choices=["debug", "info", "warn", "error", "fatal", "panic"]
|
||||
),
|
||||
quadlet_dir=dict(type="path", required=False),
|
||||
quadlet_filename=dict(type="str", required=False),
|
||||
quadlet_file_mode=dict(type="raw", required=False),
|
||||
quadlet_options=dict(type="list", elements="str", required=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_if=[
|
||||
('state', 'quadlet', ['quadlet_filename']),
|
||||
("state", "quadlet", ["quadlet_filename"]),
|
||||
],
|
||||
required_one_of=[
|
||||
('kube_file', 'kube_file_content'),
|
||||
("kube_file", "kube_file_content"),
|
||||
],
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(
|
||||
module.params['executable'], required=True)
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
manage = PodmanKubeManagement(module, executable)
|
||||
changed = False
|
||||
out = err = ''
|
||||
if module.params['state'] == 'absent':
|
||||
if manage.version is not None and LooseVersion(manage.version) > LooseVersion('3.4.0'):
|
||||
manage.module.log(msg="version: %s, kube file %s" % (manage.version, manage.module.params['kube_file']))
|
||||
out = err = ""
|
||||
if module.params["state"] == "absent":
|
||||
if manage.version is not None and LooseVersion(manage.version) > LooseVersion(
|
||||
"3.4.0"
|
||||
):
|
||||
manage.module.log(
|
||||
msg="version: %s, kube file %s"
|
||||
% (manage.version, manage.module.params["kube_file"])
|
||||
)
|
||||
changed, out, err = manage.tear_down_pods()
|
||||
else:
|
||||
pods = manage.discover_pods()
|
||||
changed, out, err = manage.remove_associated_pods(pods)
|
||||
elif module.params['state'] == 'quadlet':
|
||||
elif module.params["state"] == "quadlet":
|
||||
manage.make_quadlet()
|
||||
else:
|
||||
changed, out, err = manage.play()
|
||||
|
|
@ -480,10 +513,10 @@ def main():
|
|||
"changed": changed,
|
||||
"stdout": out,
|
||||
"stderr": err,
|
||||
"actions": manage.actions
|
||||
"actions": manage.actions,
|
||||
}
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
# flake8: noqa: E501
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: podman_pod
|
||||
short_description: Manage Podman pods
|
||||
|
|
@ -500,9 +501,9 @@ options:
|
|||
requirements:
|
||||
- "podman"
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = r'''
|
||||
RETURN = r"""
|
||||
pod:
|
||||
description: Pod inspection results for the given pod
|
||||
built.
|
||||
|
|
@ -611,9 +612,9 @@ pod:
|
|||
],
|
||||
"LockNumber": 1
|
||||
}
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r'''
|
||||
EXAMPLES = r"""
|
||||
# What modules does for example
|
||||
- containers.podman.podman_pod:
|
||||
name: pod1
|
||||
|
|
@ -693,7 +694,7 @@ EXAMPLES = r'''
|
|||
volume:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
quadlet_dir: /custom/dir
|
||||
'''
|
||||
"""
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: F402
|
||||
from ..module_utils.podman.podman_pod_lib import PodmanPodManager # noqa: F402
|
||||
from ..module_utils.podman.podman_pod_lib import ARGUMENTS_SPEC_POD # noqa: F402
|
||||
|
|
@ -705,5 +706,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
# 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
|
||||
|
||||
|
||||
|
|
@ -155,13 +156,13 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
|
||||
def get_pod_info(module, executable, name):
|
||||
command = [executable, 'pod', 'inspect']
|
||||
command = [executable, "pod", "inspect"]
|
||||
pods = [name]
|
||||
result = []
|
||||
errs = []
|
||||
rcs = []
|
||||
if not name:
|
||||
all_names = [executable, 'pod', 'ls', '-q']
|
||||
all_names = [executable, "pod", "ls", "-q"]
|
||||
rc, out, err = module.run_command(all_names)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Unable to get list of pods: %s" % err)
|
||||
|
|
@ -185,14 +186,13 @@ def get_pod_info(module, executable, name):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
name=dict(type='str')
|
||||
executable=dict(type="str", default="podman"), name=dict(type="str")
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
name = module.params["name"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
inspect_results, errs, rcs = get_pod_info(module, executable, name)
|
||||
|
||||
|
|
@ -208,5 +208,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@
|
|||
# Copyright (c) 2023, Roberto Alfieri <ralfieri@redhat.com>
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_prune
|
||||
author:
|
||||
- 'Roberto Alfieri (@rebtoor)'
|
||||
|
|
@ -91,9 +92,9 @@ options:
|
|||
https://docs.podman.io/en/latest/markdown/podman-volume-prune.1.html#filter)
|
||||
for more information on possible filters.
|
||||
type: dict
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r'''
|
||||
EXAMPLES = r"""
|
||||
- name: Prune containers older than 24h
|
||||
containers.podman.podman_prune:
|
||||
containers: true
|
||||
|
|
@ -110,9 +111,9 @@ EXAMPLES = r'''
|
|||
system: true
|
||||
system_all: true
|
||||
system_volumes: true
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = r'''
|
||||
RETURN = r"""
|
||||
# containers
|
||||
containers:
|
||||
description:
|
||||
|
|
@ -157,7 +158,7 @@ system:
|
|||
type: list
|
||||
elements: str
|
||||
sample: []
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
|
@ -165,7 +166,7 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
def filtersPrepare(target, filters):
|
||||
filter_out = []
|
||||
if target == 'system':
|
||||
if target == "system":
|
||||
for system_filter in filters:
|
||||
filter_out.append(filters[system_filter])
|
||||
else:
|
||||
|
|
@ -173,23 +174,36 @@ def filtersPrepare(target, filters):
|
|||
if isinstance(filters[common_filter], dict):
|
||||
dict_filters = filters[common_filter]
|
||||
for single_filter in dict_filters:
|
||||
filter_out.append('--filter={label}={key}={value}'.format(label=common_filter, key=single_filter,
|
||||
value=dict_filters[single_filter]))
|
||||
filter_out.append(
|
||||
"--filter={label}={key}={value}".format(
|
||||
label=common_filter,
|
||||
key=single_filter,
|
||||
value=dict_filters[single_filter],
|
||||
)
|
||||
)
|
||||
else:
|
||||
if target == 'image' and (common_filter in ('dangling_only', 'external')):
|
||||
if common_filter == 'dangling_only' and not filters['dangling_only']:
|
||||
filter_out.append('-a')
|
||||
if common_filter == 'external' and filters['external']:
|
||||
filter_out.append('--external')
|
||||
if target == "image" and (
|
||||
common_filter in ("dangling_only", "external")
|
||||
):
|
||||
if (
|
||||
common_filter == "dangling_only"
|
||||
and not filters["dangling_only"]
|
||||
):
|
||||
filter_out.append("-a")
|
||||
if common_filter == "external" and filters["external"]:
|
||||
filter_out.append("--external")
|
||||
else:
|
||||
filter_out.append('--filter={label}={value}'.format(label=common_filter,
|
||||
value=filters[common_filter]))
|
||||
filter_out.append(
|
||||
"--filter={label}={value}".format(
|
||||
label=common_filter, value=filters[common_filter]
|
||||
)
|
||||
)
|
||||
|
||||
return filter_out
|
||||
|
||||
|
||||
def podmanExec(module, target, filters, executable):
|
||||
command = [executable, target, 'prune', '--force']
|
||||
command = [executable, target, "prune", "--force"]
|
||||
if filters is not None:
|
||||
command.extend(filtersPrepare(target, filters))
|
||||
rc, out, err = module.run_command(command)
|
||||
|
|
@ -197,56 +211,61 @@ def podmanExec(module, target, filters, executable):
|
|||
|
||||
if rc != 0:
|
||||
module.fail_json(
|
||||
msg='Error executing prune on {target}: {err}'.format(target=target, err=err))
|
||||
msg="Error executing prune on {target}: {err}".format(
|
||||
target=target, err=err
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"changed": changed,
|
||||
target: list(filter(None, out.split('\n'))),
|
||||
"errors": err
|
||||
target: list(filter(None, out.split("\n"))),
|
||||
"errors": err,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
results = dict()
|
||||
module_args = dict(
|
||||
container=dict(type='bool', default=False),
|
||||
container_filters=dict(type='dict'),
|
||||
image=dict(type='bool', default=False),
|
||||
image_filters=dict(type='dict'),
|
||||
network=dict(type='bool', default=False),
|
||||
network_filters=dict(type='dict'),
|
||||
volume=dict(type='bool', default=False),
|
||||
volume_filters=dict(type='dict'),
|
||||
system=dict(type='bool', default=False),
|
||||
system_all=dict(type='bool', default=False),
|
||||
system_volumes=dict(type='bool', default=False),
|
||||
executable=dict(type='str', default='podman')
|
||||
container=dict(type="bool", default=False),
|
||||
container_filters=dict(type="dict"),
|
||||
image=dict(type="bool", default=False),
|
||||
image_filters=dict(type="dict"),
|
||||
network=dict(type="bool", default=False),
|
||||
network_filters=dict(type="dict"),
|
||||
volume=dict(type="bool", default=False),
|
||||
volume_filters=dict(type="dict"),
|
||||
system=dict(type="bool", default=False),
|
||||
system_all=dict(type="bool", default=False),
|
||||
system_volumes=dict(type="bool", default=False),
|
||||
executable=dict(type="str", default="podman"),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args
|
||||
)
|
||||
module = AnsibleModule(argument_spec=module_args)
|
||||
|
||||
executable = module.get_bin_path(
|
||||
module.params['executable'], required=True)
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
for target, filters in (
|
||||
('container', 'container_filters'), ('image', 'image_filters'), ('network', 'network_filters'),
|
||||
('volume', 'volume_filters')):
|
||||
("container", "container_filters"),
|
||||
("image", "image_filters"),
|
||||
("network", "network_filters"),
|
||||
("volume", "volume_filters"),
|
||||
):
|
||||
if module.params[target]:
|
||||
results[target] = podmanExec(module, target, module.params[filters], executable)
|
||||
results[target] = podmanExec(
|
||||
module, target, module.params[filters], executable
|
||||
)
|
||||
|
||||
if module.params['system']:
|
||||
target = 'system'
|
||||
if module.params["system"]:
|
||||
target = "system"
|
||||
system_filters = {}
|
||||
if module.params['system_all']:
|
||||
system_filters['system_all'] = '--all'
|
||||
if module.params['system_volumes']:
|
||||
system_filters['system_volumes'] = '--volumes'
|
||||
if module.params["system_all"]:
|
||||
system_filters["system_all"] = "--all"
|
||||
if module.params["system_volumes"]:
|
||||
system_filters["system_volumes"] = "--volumes"
|
||||
results[target] = podmanExec(module, target, system_filters, executable)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_runlabel
|
||||
short_description: Run given label from given image
|
||||
author: Pavel Dostal (@pdostal)
|
||||
|
|
@ -32,55 +32,53 @@ options:
|
|||
type: str
|
||||
requirements:
|
||||
- "Podman installed on host"
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
# What modules does for example
|
||||
- containers.podman.podman_runlabel:
|
||||
image: docker.io/continuumio/miniconda3
|
||||
label: INSTALL
|
||||
'''
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
||||
|
||||
|
||||
def runlabel(module, executable):
|
||||
changed = False
|
||||
command = [executable, 'container', 'runlabel']
|
||||
command.append(module.params['label'])
|
||||
command.append(module.params['image'])
|
||||
command = [executable, "container", "runlabel"]
|
||||
command.append(module.params["label"])
|
||||
command.append(module.params["image"])
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Error running the runlabel from image %s: %s" % (
|
||||
module.params['image'], err))
|
||||
module.fail_json(
|
||||
msg="Error running the runlabel from image %s: %s"
|
||||
% (module.params["image"], err)
|
||||
)
|
||||
return changed, out, err
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
image=dict(type='str', required=True),
|
||||
label=dict(type='str', required=True),
|
||||
executable=dict(type='str', default='podman')
|
||||
image=dict(type="str", required=True),
|
||||
label=dict(type="str", required=True),
|
||||
executable=dict(type="str", default="podman"),
|
||||
),
|
||||
supports_check_mode=False,
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
changed, out, err = runlabel(module, executable)
|
||||
|
||||
results = {
|
||||
"changed": changed,
|
||||
"stdout": out,
|
||||
"stderr": err
|
||||
}
|
||||
results = {"changed": changed, "stdout": out, "stderr": err}
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_save
|
||||
short_description: Saves podman image to tar file
|
||||
author: Sagi Shnaidman (@sshnaidm)
|
||||
|
|
@ -63,12 +63,12 @@ options:
|
|||
type: str
|
||||
requirements:
|
||||
- "Podman installed on host"
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
# What modules does for example
|
||||
- containers.podman.podman_save:
|
||||
image: nginx
|
||||
|
|
@ -79,7 +79,7 @@ EXAMPLES = '''
|
|||
- fedora
|
||||
dest: /tmp/file456.tar
|
||||
multi_image_archive: true
|
||||
'''
|
||||
"""
|
||||
|
||||
import os # noqa: E402
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
||||
|
|
@ -88,32 +88,32 @@ from ..module_utils.podman.common import remove_file_or_dir # noqa: E402
|
|||
|
||||
def save(module, executable):
|
||||
changed = False
|
||||
command = [executable, 'save']
|
||||
command = [executable, "save"]
|
||||
cmd_args = {
|
||||
'compress': ['--compress'],
|
||||
'dest': ['-o=%s' % module.params['dest']],
|
||||
'format': ['--format=%s' % module.params['format']],
|
||||
'multi_image_archive': ['--multi-image-archive'],
|
||||
"compress": ["--compress"],
|
||||
"dest": ["-o=%s" % module.params["dest"]],
|
||||
"format": ["--format=%s" % module.params["format"]],
|
||||
"multi_image_archive": ["--multi-image-archive"],
|
||||
}
|
||||
for param in module.params:
|
||||
if module.params[param] is not None and param in cmd_args:
|
||||
command += cmd_args[param]
|
||||
for img in module.params['image']:
|
||||
for img in module.params["image"]:
|
||||
command.append(img)
|
||||
if module.params['force']:
|
||||
if module.params["force"]:
|
||||
changed = True
|
||||
dest = module.params['dest']
|
||||
dest = module.params["dest"]
|
||||
if os.path.exists(dest):
|
||||
if module.check_mode:
|
||||
return changed, '', ''
|
||||
return changed, "", ""
|
||||
try:
|
||||
remove_file_or_dir(dest)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error deleting %s path: %s" % (dest, e))
|
||||
else:
|
||||
changed = not os.path.exists(module.params['dest'])
|
||||
changed = not os.path.exists(module.params["dest"])
|
||||
if module.check_mode:
|
||||
return changed, '', ''
|
||||
return changed, "", ""
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Error: %s" % (err))
|
||||
|
|
@ -123,20 +123,28 @@ def save(module, executable):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
image=dict(type='list', elements='str', required=True),
|
||||
compress=dict(type='bool'),
|
||||
dest=dict(type='str', required=True, aliases=['path']),
|
||||
format=dict(type='str', choices=['docker-archive', 'oci-archive', 'oci-dir', 'docker-dir']),
|
||||
multi_image_archive=dict(type='bool'),
|
||||
force=dict(type='bool', default=True),
|
||||
executable=dict(type='str', default='podman')
|
||||
image=dict(type="list", elements="str", required=True),
|
||||
compress=dict(type="bool"),
|
||||
dest=dict(type="str", required=True, aliases=["path"]),
|
||||
format=dict(
|
||||
type="str",
|
||||
choices=["docker-archive", "oci-archive", "oci-dir", "docker-dir"],
|
||||
),
|
||||
multi_image_archive=dict(type="bool"),
|
||||
force=dict(type="bool", default=True),
|
||||
executable=dict(type="str", default="podman"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
if module.params['compress'] and module.params['format'] not in ['oci-dir', 'docker-dir']:
|
||||
module.fail_json(msg="Compression is only supported for oci-dir and docker-dir format")
|
||||
if module.params["compress"] and module.params["format"] not in [
|
||||
"oci-dir",
|
||||
"docker-dir",
|
||||
]:
|
||||
module.fail_json(
|
||||
msg="Compression is only supported for oci-dir and docker-dir format"
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
changed, out, err = save(module, executable)
|
||||
|
||||
results = {
|
||||
|
|
@ -148,5 +156,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_search
|
||||
author:
|
||||
- Derek Waters (@derekwaters)
|
||||
|
|
@ -39,7 +40,7 @@ options:
|
|||
default: False
|
||||
type: bool
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Search for any rhel images
|
||||
|
|
@ -81,10 +82,10 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
|
||||
def search_images(module, executable, term, limit, list_tags):
|
||||
command = [executable, 'search', term, '--format', 'json']
|
||||
command.extend(['--limit', "{0}".format(limit)])
|
||||
command = [executable, "search", term, "--format", "json"]
|
||||
command.extend(["--limit", "{0}".format(limit)])
|
||||
if list_tags:
|
||||
command.extend(['--list-tags'])
|
||||
command.extend(["--list-tags"])
|
||||
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0 and list_tags and out == "" and "fetching tags list" in err:
|
||||
|
|
@ -97,18 +98,18 @@ def search_images(module, executable, term, limit, list_tags):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
term=dict(type='str', required=True),
|
||||
limit=dict(type='int', required=False, default=25),
|
||||
list_tags=dict(type='bool', required=False, default=False)
|
||||
executable=dict(type="str", default="podman"),
|
||||
term=dict(type="str", required=True),
|
||||
limit=dict(type="int", required=False, default=25),
|
||||
list_tags=dict(type="bool", required=False, default=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
executable = module.params['executable']
|
||||
term = module.params.get('term')
|
||||
limit = module.params.get('limit')
|
||||
list_tags = module.params.get('list_tags')
|
||||
executable = module.params["executable"]
|
||||
term = module.params.get("term")
|
||||
limit = module.params.get("limit")
|
||||
list_tags = module.params.get("list_tags")
|
||||
executable = module.get_bin_path(executable, required=True)
|
||||
|
||||
result_str, errors = search_images(module, executable, term, limit, list_tags)
|
||||
|
|
@ -118,7 +119,11 @@ def main():
|
|||
try:
|
||||
results = json.loads(result_str)
|
||||
except json.decoder.JSONDecodeError:
|
||||
module.fail_json(msg='Failed to parse JSON output from podman search: {out}'.format(out=result_str))
|
||||
module.fail_json(
|
||||
msg="Failed to parse JSON output from podman search: {out}".format(
|
||||
out=result_str
|
||||
)
|
||||
)
|
||||
|
||||
results = dict(
|
||||
changed=False,
|
||||
|
|
@ -129,5 +134,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
module: podman_secret
|
||||
author:
|
||||
|
|
@ -81,7 +82,7 @@ options:
|
|||
- Enable debug mode for module. It prints secrets diff.
|
||||
type: bool
|
||||
default: False
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Create secret
|
||||
|
|
@ -113,24 +114,30 @@ EXAMPLES = r"""
|
|||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import get_podman_version
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
LooseVersion,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
get_podman_version,
|
||||
)
|
||||
|
||||
diff = {"before": '', "after": ''}
|
||||
diff = {"before": "", "after": ""}
|
||||
|
||||
|
||||
def podman_secret_exists(module, executable, name, version):
|
||||
if version is None or LooseVersion(version) < LooseVersion('4.5.0'):
|
||||
if version is None or LooseVersion(version) < LooseVersion("4.5.0"):
|
||||
rc, out, err = module.run_command(
|
||||
[executable, 'secret', 'ls', "--format", "{{.Name}}"])
|
||||
[executable, "secret", "ls", "--format", "{{.Name}}"]
|
||||
)
|
||||
return name in [i.strip() for i in out.splitlines()]
|
||||
rc, out, err = module.run_command(
|
||||
[executable, 'secret', 'exists', name])
|
||||
rc, out, err = module.run_command([executable, "secret", "exists", name])
|
||||
return rc == 0
|
||||
|
||||
|
||||
def need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels):
|
||||
cmd = [executable, 'secret', 'inspect', '--showsecret', name]
|
||||
def need_update(
|
||||
module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels
|
||||
):
|
||||
cmd = [executable, "secret", "inspect", "--showsecret", name]
|
||||
rc, out, err = module.run_command(cmd)
|
||||
if rc != 0:
|
||||
if debug:
|
||||
|
|
@ -141,64 +148,90 @@ def need_update(module, executable, name, data, path, env, skip, driver, driver_
|
|||
try:
|
||||
secret = module.from_json(out)[0]
|
||||
if data:
|
||||
if secret['SecretData'] != data:
|
||||
if secret["SecretData"] != data:
|
||||
if debug:
|
||||
diff['after'] = data
|
||||
diff['before'] = secret['SecretData']
|
||||
diff["after"] = data
|
||||
diff["before"] = secret["SecretData"]
|
||||
else:
|
||||
diff['after'] = "<different-secret>"
|
||||
diff['before'] = "<secret>"
|
||||
diff["after"] = "<different-secret>"
|
||||
diff["before"] = "<secret>"
|
||||
return True
|
||||
if path:
|
||||
with open(path, 'rb') as f:
|
||||
text = f.read().decode('utf-8')
|
||||
if secret['SecretData'] != text:
|
||||
with open(path, "rb") as f:
|
||||
text = f.read().decode("utf-8")
|
||||
if secret["SecretData"] != text:
|
||||
if debug:
|
||||
diff['after'] = text
|
||||
diff['before'] = secret['SecretData']
|
||||
diff["after"] = text
|
||||
diff["before"] = secret["SecretData"]
|
||||
else:
|
||||
diff['after'] = "<different-secret>"
|
||||
diff['before'] = "<secret>"
|
||||
diff["after"] = "<different-secret>"
|
||||
diff["before"] = "<secret>"
|
||||
return True
|
||||
if env:
|
||||
env_data = os.environ.get(env)
|
||||
if secret['SecretData'] != env_data:
|
||||
if secret["SecretData"] != env_data:
|
||||
if debug:
|
||||
diff['after'] = env_data
|
||||
diff['before'] = secret['SecretData']
|
||||
diff["after"] = env_data
|
||||
diff["before"] = secret["SecretData"]
|
||||
else:
|
||||
diff['after'] = "<different-secret>"
|
||||
diff['before'] = "<secret>"
|
||||
diff["after"] = "<different-secret>"
|
||||
diff["before"] = "<secret>"
|
||||
return True
|
||||
if driver:
|
||||
if secret['Spec']['Driver']['Name'] != driver:
|
||||
diff['after'] = driver
|
||||
diff['before'] = secret['Spec']['Driver']['Name']
|
||||
if secret["Spec"]["Driver"]["Name"] != driver:
|
||||
diff["after"] = driver
|
||||
diff["before"] = secret["Spec"]["Driver"]["Name"]
|
||||
return True
|
||||
if driver_opts:
|
||||
for k, v in driver_opts.items():
|
||||
if secret['Spec']['Driver']['Options'].get(k) != v:
|
||||
diff['after'] = "=".join([k, v])
|
||||
diff['before'] = "=".join(
|
||||
[k, secret['Spec']['Driver']['Options'].get(k)])
|
||||
if secret["Spec"]["Driver"]["Options"].get(k) != v:
|
||||
diff["after"] = "=".join([k, v])
|
||||
diff["before"] = "=".join(
|
||||
[k, secret["Spec"]["Driver"]["Options"].get(k)]
|
||||
)
|
||||
return True
|
||||
if labels:
|
||||
for k, v in labels.items():
|
||||
if secret['Spec']['Labels'].get(k) != v:
|
||||
diff['after'] = "=".join([k, v])
|
||||
diff['before'] = "=".join(
|
||||
[k, secret['Spec']['Labels'].get(k)])
|
||||
if secret["Spec"]["Labels"].get(k) != v:
|
||||
diff["after"] = "=".join([k, v])
|
||||
diff["before"] = "=".join([k, secret["Spec"]["Labels"].get(k)])
|
||||
return True
|
||||
except Exception:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def podman_secret_create(module, executable, name, data, path, env, force, skip,
|
||||
driver, driver_opts, debug, labels):
|
||||
def podman_secret_create(
|
||||
module,
|
||||
executable,
|
||||
name,
|
||||
data,
|
||||
path,
|
||||
env,
|
||||
force,
|
||||
skip,
|
||||
driver,
|
||||
driver_opts,
|
||||
debug,
|
||||
labels,
|
||||
):
|
||||
podman_version = get_podman_version(module, fail=False)
|
||||
if podman_version is not None and LooseVersion(podman_version) >= LooseVersion('4.7.0'):
|
||||
if need_update(module, executable, name, data, path, env, skip, driver, driver_opts, debug, labels):
|
||||
if podman_version is not None and LooseVersion(podman_version) >= LooseVersion(
|
||||
"4.7.0"
|
||||
):
|
||||
if need_update(
|
||||
module,
|
||||
executable,
|
||||
name,
|
||||
data,
|
||||
path,
|
||||
env,
|
||||
skip,
|
||||
driver,
|
||||
driver_opts,
|
||||
debug,
|
||||
labels,
|
||||
):
|
||||
podman_secret_remove(module, executable, name)
|
||||
else:
|
||||
return {"changed": False}
|
||||
|
|
@ -208,20 +241,20 @@ def podman_secret_create(module, executable, name, data, path, env, force, skip,
|
|||
if skip and podman_secret_exists(module, executable, name, podman_version):
|
||||
return {"changed": False}
|
||||
|
||||
cmd = [executable, 'secret', 'create']
|
||||
cmd = [executable, "secret", "create"]
|
||||
if driver:
|
||||
cmd.append('--driver')
|
||||
cmd.append("--driver")
|
||||
cmd.append(driver)
|
||||
if driver_opts:
|
||||
cmd.append('--driver-opts')
|
||||
cmd.append("--driver-opts")
|
||||
cmd.append(",".join("=".join(i) for i in driver_opts.items()))
|
||||
if labels:
|
||||
for k, v in labels.items():
|
||||
cmd.append('--label')
|
||||
cmd.append("--label")
|
||||
cmd.append("=".join([k, v]))
|
||||
cmd.append(name)
|
||||
if data:
|
||||
cmd.append('-')
|
||||
cmd.append("-")
|
||||
elif path:
|
||||
cmd.append(path)
|
||||
elif env:
|
||||
|
|
@ -240,18 +273,18 @@ def podman_secret_create(module, executable, name, data, path, env, force, skip,
|
|||
return {
|
||||
"changed": True,
|
||||
"diff": {
|
||||
"before": diff['before'] + "\n",
|
||||
"after": diff['after'] + "\n",
|
||||
"before": diff["before"] + "\n",
|
||||
"after": diff["after"] + "\n",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def podman_secret_remove(module, executable, name):
|
||||
changed = False
|
||||
rc, out, err = module.run_command([executable, 'secret', 'rm', name])
|
||||
rc, out, err = module.run_command([executable, "secret", "rm", name])
|
||||
if rc == 0:
|
||||
changed = True
|
||||
elif 'no such secret' in err:
|
||||
elif "no such secret" in err:
|
||||
pass
|
||||
else:
|
||||
module.fail_json(msg="Unable to remove secret: %s" % err)
|
||||
|
|
@ -264,45 +297,56 @@ def podman_secret_remove(module, executable, name):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
name=dict(type='str', required=True),
|
||||
data=dict(type='str', no_log=True),
|
||||
env=dict(type='str'),
|
||||
path=dict(type='path'),
|
||||
force=dict(type='bool', default=False),
|
||||
skip_existing=dict(type='bool', default=False),
|
||||
driver=dict(type='str'),
|
||||
driver_opts=dict(type='dict'),
|
||||
labels=dict(type='dict'),
|
||||
debug=dict(type='bool', default=False),
|
||||
executable=dict(type="str", default="podman"),
|
||||
state=dict(type="str", default="present", choices=["absent", "present"]),
|
||||
name=dict(type="str", required=True),
|
||||
data=dict(type="str", no_log=True),
|
||||
env=dict(type="str"),
|
||||
path=dict(type="path"),
|
||||
force=dict(type="bool", default=False),
|
||||
skip_existing=dict(type="bool", default=False),
|
||||
driver=dict(type="str"),
|
||||
driver_opts=dict(type="dict"),
|
||||
labels=dict(type="dict"),
|
||||
debug=dict(type="bool", default=False),
|
||||
),
|
||||
required_if=[('state', 'present', ['path', 'env', 'data'], True)],
|
||||
mutually_exclusive=[['path', 'env', 'data']],
|
||||
required_if=[("state", "present", ["path", "env", "data"], True)],
|
||||
mutually_exclusive=[["path", "env", "data"]],
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
name = module.params['name']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
state = module.params["state"]
|
||||
name = module.params["name"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
if state == 'present':
|
||||
data = module.params['data']
|
||||
force = module.params['force']
|
||||
skip = module.params['skip_existing']
|
||||
driver = module.params['driver']
|
||||
driver_opts = module.params['driver_opts']
|
||||
debug = module.params['debug']
|
||||
labels = module.params['labels']
|
||||
path = module.params['path']
|
||||
env = module.params['env']
|
||||
results = podman_secret_create(module, executable,
|
||||
name, data, path, env, force, skip,
|
||||
driver, driver_opts, debug, labels)
|
||||
if state == "present":
|
||||
data = module.params["data"]
|
||||
force = module.params["force"]
|
||||
skip = module.params["skip_existing"]
|
||||
driver = module.params["driver"]
|
||||
driver_opts = module.params["driver_opts"]
|
||||
debug = module.params["debug"]
|
||||
labels = module.params["labels"]
|
||||
path = module.params["path"]
|
||||
env = module.params["env"]
|
||||
results = podman_secret_create(
|
||||
module,
|
||||
executable,
|
||||
name,
|
||||
data,
|
||||
path,
|
||||
env,
|
||||
force,
|
||||
skip,
|
||||
driver,
|
||||
driver_opts,
|
||||
debug,
|
||||
labels,
|
||||
)
|
||||
else:
|
||||
results = podman_secret_remove(module, executable, name)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_secret_info
|
||||
author:
|
||||
- "Sagi Shnaidman (@sshnaidm)"
|
||||
|
|
@ -32,7 +33,7 @@ options:
|
|||
machine running C(podman)
|
||||
default: 'podman'
|
||||
type: str
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Gather info about all present secrets
|
||||
|
|
@ -72,21 +73,23 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
|
||||
def get_secret_info(module, executable, show, name):
|
||||
command = [executable, 'secret', 'inspect']
|
||||
command = [executable, "secret", "inspect"]
|
||||
if show:
|
||||
command.append('--showsecret')
|
||||
command.append("--showsecret")
|
||||
if name:
|
||||
command.append(name)
|
||||
else:
|
||||
all_names = [executable, 'secret', 'ls', '-q']
|
||||
all_names = [executable, "secret", "ls", "-q"]
|
||||
rc, out, err = module.run_command(all_names)
|
||||
name = out.split()
|
||||
if not name:
|
||||
return [], out, err
|
||||
command.extend(name)
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0 or 'no secret with name or id' in err:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all secrets', err))
|
||||
if rc != 0 or "no secret with name or id" in err:
|
||||
module.fail_json(
|
||||
msg="Unable to gather info for %s: %s" % (name or "all secrets", err)
|
||||
)
|
||||
if not out or json.loads(out) is None:
|
||||
return [], out, err
|
||||
return json.loads(out), out, err
|
||||
|
|
@ -95,16 +98,16 @@ def get_secret_info(module, executable, show, name):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
name=dict(type='str'),
|
||||
showsecret=dict(type='bool', default=False),
|
||||
executable=dict(type="str", default="podman"),
|
||||
name=dict(type="str"),
|
||||
showsecret=dict(type="bool", default=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
showsecret = module.params['showsecret']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
name = module.params["name"]
|
||||
showsecret = module.params["showsecret"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
inspect_results, out, err = get_secret_info(module, executable, showsecret, name)
|
||||
|
||||
|
|
@ -117,5 +120,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_tag
|
||||
short_description: Add an additional name to a local image
|
||||
author: Christian Bourque (@ocafebabe)
|
||||
|
|
@ -33,50 +33,51 @@ options:
|
|||
type: str
|
||||
requirements:
|
||||
- "Podman installed on host"
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
# What modules does for example
|
||||
- containers.podman.podman_tag:
|
||||
image: docker.io/continuumio/miniconda3
|
||||
target_names:
|
||||
- miniconda3
|
||||
- miniconda
|
||||
'''
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: E402
|
||||
|
||||
|
||||
def tag(module, executable):
|
||||
changed = False
|
||||
command = [executable, 'tag']
|
||||
command.append(module.params['image'])
|
||||
command.extend(module.params['target_names'])
|
||||
command = [executable, "tag"]
|
||||
command.append(module.params["image"])
|
||||
command.extend(module.params["target_names"])
|
||||
if module.check_mode:
|
||||
return changed, '', ''
|
||||
return changed, "", ""
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc == 0:
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="Error tagging local image %s: %s" % (
|
||||
module.params['image'], err))
|
||||
module.fail_json(
|
||||
msg="Error tagging local image %s: %s" % (module.params["image"], err)
|
||||
)
|
||||
return changed, out, err
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
image=dict(type='str', required=True),
|
||||
target_names=dict(type='list', elements='str', required=True),
|
||||
executable=dict(type='str', default='podman')
|
||||
image=dict(type="str", required=True),
|
||||
target_names=dict(type="list", elements="str", required=True),
|
||||
executable=dict(type="str", default="podman"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
changed, out, err = tag(module, executable)
|
||||
|
||||
results = {
|
||||
|
|
@ -87,5 +88,5 @@ def main():
|
|||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
# flake8: noqa: E501
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: podman_volume
|
||||
short_description: Manage Podman volumes
|
||||
|
|
@ -101,9 +102,9 @@ options:
|
|||
requirements:
|
||||
- "podman"
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
RETURN = '''
|
||||
RETURN = """
|
||||
volume:
|
||||
description: Volume inspection results if exists.
|
||||
returned: always
|
||||
|
|
@ -121,9 +122,9 @@ volume:
|
|||
Options: {}
|
||||
Scope: local
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = '''
|
||||
EXAMPLES = """
|
||||
# What modules does for example
|
||||
- name: Create a volume
|
||||
containers.podman.podman_volume:
|
||||
|
|
@ -147,26 +148,32 @@ EXAMPLES = '''
|
|||
- Copy=true
|
||||
- Image=quay.io/centos/centos:latest
|
||||
|
||||
'''
|
||||
"""
|
||||
# noqa: F402
|
||||
import json # noqa: F402
|
||||
import os # noqa: F402
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule # noqa: F402
|
||||
from ansible.module_utils._text import to_bytes, to_native # noqa: F402
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import LooseVersion
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import create_quadlet_state
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
LooseVersion,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
||||
lower_keys,
|
||||
)
|
||||
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import (
|
||||
create_quadlet_state,
|
||||
)
|
||||
|
||||
|
||||
class PodmanVolumeModuleParams:
|
||||
"""Creates list of arguments for podman CLI command.
|
||||
|
||||
Arguments:
|
||||
action {str} -- action type from 'create', 'delete'
|
||||
params {dict} -- dictionary of module parameters
|
||||
Arguments:
|
||||
action {str} -- action type from 'create', 'delete'
|
||||
params {dict} -- dictionary of module parameters
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, action, params, podman_version, module):
|
||||
self.params = params
|
||||
|
|
@ -180,58 +187,64 @@ class PodmanVolumeModuleParams:
|
|||
Returns:
|
||||
list -- list of byte strings for Popen command
|
||||
"""
|
||||
if self.action in ['delete', 'mount', 'unmount']:
|
||||
if self.action in ["delete", "mount", "unmount"]:
|
||||
return self._simple_action()
|
||||
if self.action in ['create']:
|
||||
if self.action in ["create"]:
|
||||
return self._create_action()
|
||||
|
||||
def _simple_action(self):
|
||||
if self.action == 'delete':
|
||||
cmd = ['rm', '-f', self.params['name']]
|
||||
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
|
||||
if self.action == 'mount':
|
||||
cmd = ['mount', self.params['name']]
|
||||
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
|
||||
if self.action == 'unmount':
|
||||
cmd = ['unmount', self.params['name']]
|
||||
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
|
||||
if self.action == "delete":
|
||||
cmd = ["rm", "-f", self.params["name"]]
|
||||
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
||||
if self.action == "mount":
|
||||
cmd = ["mount", self.params["name"]]
|
||||
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
||||
if self.action == "unmount":
|
||||
cmd = ["unmount", self.params["name"]]
|
||||
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
||||
|
||||
def _create_action(self):
|
||||
cmd = [self.action, self.params['name']]
|
||||
all_param_methods = [func for func in dir(self)
|
||||
if callable(getattr(self, func))
|
||||
and func.startswith("addparam")]
|
||||
cmd = [self.action, self.params["name"]]
|
||||
all_param_methods = [
|
||||
func
|
||||
for func in dir(self)
|
||||
if callable(getattr(self, func)) and func.startswith("addparam")
|
||||
]
|
||||
params_set = (i for i in self.params if self.params[i] is not None)
|
||||
for param in params_set:
|
||||
func_name = "_".join(["addparam", param])
|
||||
if func_name in all_param_methods:
|
||||
cmd = getattr(self, func_name)(cmd)
|
||||
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
|
||||
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
||||
|
||||
def check_version(self, param, minv=None, maxv=None):
|
||||
if minv and LooseVersion(minv) > LooseVersion(
|
||||
self.podman_version):
|
||||
self.module.fail_json(msg="Parameter %s is supported from podman "
|
||||
"version %s only! Current version is %s" % (
|
||||
param, minv, self.podman_version))
|
||||
if maxv and LooseVersion(maxv) < LooseVersion(
|
||||
self.podman_version):
|
||||
self.module.fail_json(msg="Parameter %s is supported till podman "
|
||||
"version %s only! Current version is %s" % (
|
||||
param, minv, self.podman_version))
|
||||
if minv and LooseVersion(minv) > LooseVersion(self.podman_version):
|
||||
self.module.fail_json(
|
||||
msg="Parameter %s is supported from podman "
|
||||
"version %s only! Current version is %s"
|
||||
% (param, minv, self.podman_version)
|
||||
)
|
||||
if maxv and LooseVersion(maxv) < LooseVersion(self.podman_version):
|
||||
self.module.fail_json(
|
||||
msg="Parameter %s is supported till podman "
|
||||
"version %s only! Current version is %s"
|
||||
% (param, minv, self.podman_version)
|
||||
)
|
||||
|
||||
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])]
|
||||
for label in self.params["label"].items():
|
||||
c += [
|
||||
"--label",
|
||||
b"=".join([to_bytes(l, errors="surrogate_or_strict") for l in label]),
|
||||
]
|
||||
return c
|
||||
|
||||
def addparam_driver(self, c):
|
||||
return c + ['--driver', self.params['driver']]
|
||||
return c + ["--driver", self.params["driver"]]
|
||||
|
||||
def addparam_options(self, c):
|
||||
for opt in self.params['options']:
|
||||
c += ['--opt', opt]
|
||||
for opt in self.params["options"]:
|
||||
c += ["--opt", opt]
|
||||
return c
|
||||
|
||||
|
||||
|
|
@ -239,11 +252,7 @@ class PodmanVolumeDefaults:
|
|||
def __init__(self, module, podman_version):
|
||||
self.module = module
|
||||
self.version = podman_version
|
||||
self.defaults = {
|
||||
'driver': 'local',
|
||||
'label': {},
|
||||
'options': {}
|
||||
}
|
||||
self.defaults = {"driver": "local", "label": {}, "options": {}}
|
||||
|
||||
def default_dict(self):
|
||||
# make here any changes to self.defaults related to podman version
|
||||
|
|
@ -257,13 +266,14 @@ class PodmanVolumeDiff:
|
|||
self.default_dict = None
|
||||
self.info = lower_keys(info)
|
||||
self.params = self.defaultize()
|
||||
self.diff = {'before': {}, 'after': {}}
|
||||
self.diff = {"before": {}, "after": {}}
|
||||
self.non_idempotent = {}
|
||||
|
||||
def defaultize(self):
|
||||
params_with_defaults = {}
|
||||
self.default_dict = PodmanVolumeDefaults(
|
||||
self.module, self.version).default_dict()
|
||||
self.module, self.version
|
||||
).default_dict()
|
||||
for p in self.module.params:
|
||||
if self.module.params[p] is None and p in self.default_dict:
|
||||
params_with_defaults[p] = self.default_dict[p]
|
||||
|
|
@ -273,29 +283,29 @@ class PodmanVolumeDiff:
|
|||
|
||||
def _diff_update_and_compare(self, param_name, before, after):
|
||||
if before != after:
|
||||
self.diff['before'].update({param_name: before})
|
||||
self.diff['after'].update({param_name: after})
|
||||
self.diff["before"].update({param_name: before})
|
||||
self.diff["after"].update({param_name: after})
|
||||
return True
|
||||
return False
|
||||
|
||||
def diffparam_label(self):
|
||||
before = self.info['labels'] if 'labels' in self.info else {}
|
||||
after = self.params['label']
|
||||
return self._diff_update_and_compare('label', before, after)
|
||||
before = self.info["labels"] if "labels" in self.info else {}
|
||||
after = self.params["label"]
|
||||
return self._diff_update_and_compare("label", before, after)
|
||||
|
||||
def diffparam_driver(self):
|
||||
before = self.info['driver']
|
||||
after = self.params['driver']
|
||||
return self._diff_update_and_compare('driver', before, after)
|
||||
before = self.info["driver"]
|
||||
after = self.params["driver"]
|
||||
return self._diff_update_and_compare("driver", before, after)
|
||||
|
||||
def diffparam_options(self):
|
||||
before = self.info['options'] if 'options' in self.info else {}
|
||||
before = self.info["options"] if "options" in self.info else {}
|
||||
# Removing GID and UID from options list
|
||||
before.pop('uid', None)
|
||||
before.pop('gid', None)
|
||||
before.pop("uid", None)
|
||||
before.pop("gid", None)
|
||||
# Collecting all other options in the list
|
||||
before = ["=".join((k, v)) for k, v in before.items()]
|
||||
after = self.params['options']
|
||||
after = self.params["options"]
|
||||
# # For UID, GID
|
||||
# if 'uid' in self.info or 'gid' in self.info:
|
||||
# ids = []
|
||||
|
|
@ -312,12 +322,14 @@ class PodmanVolumeDiff:
|
|||
# after = [i for i in after if 'gid' not in i and 'uid' not in i]
|
||||
# after += ids
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('options', before, after)
|
||||
return self._diff_update_and_compare("options", before, after)
|
||||
|
||||
def is_different(self):
|
||||
diff_func_list = [func for func in dir(self)
|
||||
if callable(getattr(self, func)) and func.startswith(
|
||||
"diffparam")]
|
||||
diff_func_list = [
|
||||
func
|
||||
for func in dir(self)
|
||||
if callable(getattr(self, func)) and func.startswith("diffparam")
|
||||
]
|
||||
fail_fast = not bool(self.module._diff)
|
||||
different = False
|
||||
for func_name in diff_func_list:
|
||||
|
|
@ -329,7 +341,11 @@ class PodmanVolumeDiff:
|
|||
different = True
|
||||
# Check non idempotent parameters
|
||||
for p in self.non_idempotent:
|
||||
if self.module.params[p] is not None and self.module.params[p] not in [{}, [], '']:
|
||||
if self.module.params[p] is not None and self.module.params[p] not in [
|
||||
{},
|
||||
[],
|
||||
"",
|
||||
]:
|
||||
different = True
|
||||
return different
|
||||
|
||||
|
|
@ -351,7 +367,7 @@ class PodmanVolume:
|
|||
super(PodmanVolume, self).__init__()
|
||||
self.module = module
|
||||
self.name = name
|
||||
self.stdout, self.stderr = '', ''
|
||||
self.stdout, self.stderr = "", ""
|
||||
self.mount_point = None
|
||||
self.info = self.get_info()
|
||||
self.version = self._get_podman_version()
|
||||
|
|
@ -366,26 +382,30 @@ class PodmanVolume:
|
|||
@property
|
||||
def different(self):
|
||||
"""Check if volume is different."""
|
||||
diffcheck = PodmanVolumeDiff(
|
||||
self.module,
|
||||
self.info,
|
||||
self.version)
|
||||
diffcheck = PodmanVolumeDiff(self.module, self.info, self.version)
|
||||
is_different = diffcheck.is_different()
|
||||
diffs = diffcheck.diff
|
||||
if self.module._diff and is_different and diffs['before'] and diffs['after']:
|
||||
self.diff['before'] = "\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(
|
||||
diffs['before'].items())]) + "\n"
|
||||
self.diff['after'] = "\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(
|
||||
diffs['after'].items())]) + "\n"
|
||||
if self.module._diff and is_different and diffs["before"] and diffs["after"]:
|
||||
self.diff["before"] = (
|
||||
"\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(diffs["before"].items())]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
self.diff["after"] = (
|
||||
"\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(diffs["after"].items())]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
return is_different
|
||||
|
||||
def get_info(self):
|
||||
"""Inspect volume and gather info about it."""
|
||||
# pylint: disable=unused-variable
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'volume', b'inspect', self.name])
|
||||
[self.module.params["executable"], b"volume", b"inspect", self.name]
|
||||
)
|
||||
if rc == 0:
|
||||
data = json.loads(out)
|
||||
if data:
|
||||
|
|
@ -397,10 +417,12 @@ class PodmanVolume:
|
|||
def _get_podman_version(self):
|
||||
# pylint: disable=unused-variable
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'--version'])
|
||||
[self.module.params["executable"], b"--version"]
|
||||
)
|
||||
if rc != 0 or not out or "version" not in out:
|
||||
self.module.fail_json(msg="%s run failed!" %
|
||||
self.module.params['executable'])
|
||||
self.module.fail_json(
|
||||
msg="%s run failed!" % self.module.params["executable"]
|
||||
)
|
||||
return out.split("version")[1].strip()
|
||||
|
||||
def _perform_action(self, action):
|
||||
|
|
@ -409,47 +431,50 @@ class PodmanVolume:
|
|||
Arguments:
|
||||
action {str} -- action to perform - create, delete, mount, unmout
|
||||
"""
|
||||
b_command = PodmanVolumeModuleParams(action,
|
||||
self.module.params,
|
||||
self.version,
|
||||
self.module,
|
||||
).construct_command_from_params()
|
||||
full_cmd = " ".join([self.module.params['executable'], 'volume']
|
||||
+ [to_native(i) for i in b_command])
|
||||
b_command = PodmanVolumeModuleParams(
|
||||
action,
|
||||
self.module.params,
|
||||
self.version,
|
||||
self.module,
|
||||
).construct_command_from_params()
|
||||
full_cmd = " ".join(
|
||||
[self.module.params["executable"], "volume"]
|
||||
+ [to_native(i) for i in b_command]
|
||||
)
|
||||
# check if running not from root
|
||||
if os.getuid() != 0 and action == 'mount':
|
||||
if os.getuid() != 0 and action == "mount":
|
||||
full_cmd = f"{self.module.params['executable']} unshare {full_cmd}"
|
||||
self.module.log("PODMAN-VOLUME-DEBUG: %s" % full_cmd)
|
||||
self.actions.append(full_cmd)
|
||||
if not self.module.check_mode:
|
||||
rc, out, err = self.module.run_command(
|
||||
full_cmd,
|
||||
expand_user_and_vars=False)
|
||||
rc, out, err = self.module.run_command(full_cmd, expand_user_and_vars=False)
|
||||
self.stdout = out
|
||||
self.stderr = err
|
||||
if rc != 0:
|
||||
self.module.fail_json(
|
||||
msg="Can't %s volume %s" % (action, self.name),
|
||||
stdout=out, stderr=err)
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
)
|
||||
# in case of mount/unmount, return path to the volume from stdout
|
||||
if action in ['mount']:
|
||||
if action in ["mount"]:
|
||||
self.mount_point = out.strip()
|
||||
|
||||
def delete(self):
|
||||
"""Delete the volume."""
|
||||
self._perform_action('delete')
|
||||
self._perform_action("delete")
|
||||
|
||||
def create(self):
|
||||
"""Create the volume."""
|
||||
self._perform_action('create')
|
||||
self._perform_action("create")
|
||||
|
||||
def mount(self):
|
||||
"""Delete the volume."""
|
||||
self._perform_action('mount')
|
||||
self._perform_action("mount")
|
||||
|
||||
def unmount(self):
|
||||
"""Create the volume."""
|
||||
self._perform_action('unmount')
|
||||
self._perform_action("unmount")
|
||||
|
||||
def recreate(self):
|
||||
"""Recreate the volume."""
|
||||
|
|
@ -474,16 +499,16 @@ class PodmanVolumeManager:
|
|||
|
||||
self.module = module
|
||||
self.results = {
|
||||
'changed': False,
|
||||
'actions': [],
|
||||
'volume': {},
|
||||
"changed": False,
|
||||
"actions": [],
|
||||
"volume": {},
|
||||
}
|
||||
self.name = self.module.params['name']
|
||||
self.executable = \
|
||||
self.module.get_bin_path(self.module.params['executable'],
|
||||
required=True)
|
||||
self.state = self.module.params['state']
|
||||
self.recreate = self.module.params['recreate']
|
||||
self.name = self.module.params["name"]
|
||||
self.executable = self.module.get_bin_path(
|
||||
self.module.params["executable"], required=True
|
||||
)
|
||||
self.state = self.module.params["state"]
|
||||
self.recreate = self.module.params["recreate"]
|
||||
self.volume = PodmanVolume(self.module, self.name)
|
||||
|
||||
def update_volume_result(self, changed=True):
|
||||
|
|
@ -495,39 +520,45 @@ class PodmanVolumeManager:
|
|||
"""
|
||||
facts = self.volume.get_info() if changed else self.volume.info
|
||||
out, err = self.volume.stdout, self.volume.stderr
|
||||
self.results.update({'changed': changed, 'volume': facts,
|
||||
'podman_actions': self.volume.actions},
|
||||
stdout=out, stderr=err)
|
||||
self.results.update(
|
||||
{
|
||||
"changed": changed,
|
||||
"volume": facts,
|
||||
"podman_actions": self.volume.actions,
|
||||
},
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
)
|
||||
if self.volume.diff:
|
||||
self.results.update({'diff': self.volume.diff})
|
||||
if self.module.params['debug']:
|
||||
self.results.update({'podman_version': self.volume.version})
|
||||
self.results.update({"diff": self.volume.diff})
|
||||
if self.module.params["debug"]:
|
||||
self.results.update({"podman_version": self.volume.version})
|
||||
self.module.exit_json(**self.results)
|
||||
|
||||
def execute(self):
|
||||
"""Execute the desired action according to map of actions & states."""
|
||||
states_map = {
|
||||
'present': self.make_present,
|
||||
'absent': self.make_absent,
|
||||
'mounted': self.make_mount,
|
||||
'unmounted': self.make_unmount,
|
||||
'quadlet': self.make_quadlet,
|
||||
"present": self.make_present,
|
||||
"absent": self.make_absent,
|
||||
"mounted": self.make_mount,
|
||||
"unmounted": self.make_unmount,
|
||||
"quadlet": self.make_quadlet,
|
||||
}
|
||||
process_action = states_map[self.state]
|
||||
process_action()
|
||||
self.module.fail_json(msg="Unexpected logic error happened, "
|
||||
"please contact maintainers ASAP!")
|
||||
self.module.fail_json(
|
||||
msg="Unexpected logic error happened, " "please contact maintainers ASAP!"
|
||||
)
|
||||
|
||||
def make_present(self):
|
||||
"""Run actions if desired state is 'started'."""
|
||||
if not self.volume.exists:
|
||||
self.volume.create()
|
||||
self.results['actions'].append('created %s' % self.volume.name)
|
||||
self.results["actions"].append("created %s" % self.volume.name)
|
||||
self.update_volume_result()
|
||||
elif self.recreate or self.volume.different:
|
||||
self.volume.recreate()
|
||||
self.results['actions'].append('recreated %s' %
|
||||
self.volume.name)
|
||||
self.results["actions"].append("recreated %s" % self.volume.name)
|
||||
self.update_volume_result()
|
||||
else:
|
||||
self.update_volume_result(changed=False)
|
||||
|
|
@ -535,31 +566,30 @@ class PodmanVolumeManager:
|
|||
def make_absent(self):
|
||||
"""Run actions if desired state is 'absent'."""
|
||||
if not self.volume.exists:
|
||||
self.results.update({'changed': False})
|
||||
self.results.update({"changed": False})
|
||||
elif self.volume.exists:
|
||||
self.volume.delete()
|
||||
self.results['actions'].append('deleted %s' % self.volume.name)
|
||||
self.results.update({'changed': True})
|
||||
self.results.update({'volume': {},
|
||||
'podman_actions': self.volume.actions})
|
||||
self.results["actions"].append("deleted %s" % self.volume.name)
|
||||
self.results.update({"changed": True})
|
||||
self.results.update({"volume": {}, "podman_actions": self.volume.actions})
|
||||
self.module.exit_json(**self.results)
|
||||
|
||||
def make_mount(self):
|
||||
"""Run actions if desired state is 'mounted'."""
|
||||
if not self.volume.exists:
|
||||
self.volume.create()
|
||||
self.results['actions'].append('created %s' % self.volume.name)
|
||||
self.results["actions"].append("created %s" % self.volume.name)
|
||||
self.volume.mount()
|
||||
self.results['actions'].append('mounted %s' % self.volume.name)
|
||||
self.results["actions"].append("mounted %s" % self.volume.name)
|
||||
if self.volume.mount_point:
|
||||
self.results.update({'mount_point': self.volume.mount_point})
|
||||
self.results.update({"mount_point": self.volume.mount_point})
|
||||
self.update_volume_result()
|
||||
|
||||
def make_unmount(self):
|
||||
"""Run actions if desired state is 'unmounted'."""
|
||||
if self.volume.exists:
|
||||
self.volume.unmount()
|
||||
self.results['actions'].append('unmounted %s' % self.volume.name)
|
||||
self.results["actions"].append("unmounted %s" % self.volume.name)
|
||||
self.update_volume_result()
|
||||
else:
|
||||
self.module.fail_json(msg="Volume %s does not exist!" % self.name)
|
||||
|
|
@ -573,23 +603,27 @@ class PodmanVolumeManager:
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
state=dict(type='str', default="present",
|
||||
choices=['present', 'absent', 'mounted', 'unmounted', 'quadlet']),
|
||||
name=dict(type='str', required=True),
|
||||
label=dict(type='dict', required=False),
|
||||
driver=dict(type='str', required=False),
|
||||
options=dict(type='list', elements='str', required=False),
|
||||
recreate=dict(type='bool', default=False),
|
||||
executable=dict(type='str', required=False, default='podman'),
|
||||
debug=dict(type='bool', default=False),
|
||||
quadlet_dir=dict(type='path', required=False),
|
||||
quadlet_filename=dict(type='str', required=False),
|
||||
quadlet_file_mode=dict(type='raw', required=False),
|
||||
quadlet_options=dict(type='list', elements='str', required=False),
|
||||
))
|
||||
state=dict(
|
||||
type="str",
|
||||
default="present",
|
||||
choices=["present", "absent", "mounted", "unmounted", "quadlet"],
|
||||
),
|
||||
name=dict(type="str", required=True),
|
||||
label=dict(type="dict", required=False),
|
||||
driver=dict(type="str", required=False),
|
||||
options=dict(type="list", elements="str", required=False),
|
||||
recreate=dict(type="bool", default=False),
|
||||
executable=dict(type="str", required=False, default="podman"),
|
||||
debug=dict(type="bool", default=False),
|
||||
quadlet_dir=dict(type="path", required=False),
|
||||
quadlet_filename=dict(type="str", required=False),
|
||||
quadlet_file_mode=dict(type="raw", required=False),
|
||||
quadlet_options=dict(type="list", elements="str", required=False),
|
||||
)
|
||||
)
|
||||
|
||||
PodmanVolumeManager(module).execute()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
# 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 = r'''
|
||||
DOCUMENTATION = r"""
|
||||
module: podman_volume_info
|
||||
author:
|
||||
- "Sagi Shnaidman (@sshnaidm)"
|
||||
|
|
@ -27,7 +28,7 @@ options:
|
|||
machine running C(podman)
|
||||
default: 'podman'
|
||||
type: str
|
||||
'''
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Gather info about all present volumes
|
||||
|
|
@ -65,14 +66,16 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
|
||||
|
||||
def get_volume_info(module, executable, name):
|
||||
command = [executable, 'volume', 'inspect']
|
||||
command = [executable, "volume", "inspect"]
|
||||
if name:
|
||||
command.append(name)
|
||||
else:
|
||||
command.append("--all")
|
||||
rc, out, err = module.run_command(command)
|
||||
if rc != 0 or 'no such volume' in err:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all volumes', err))
|
||||
if rc != 0 or "no such volume" in err:
|
||||
module.fail_json(
|
||||
msg="Unable to gather info for %s: %s" % (name or "all volumes", err)
|
||||
)
|
||||
if not out or json.loads(out) is None:
|
||||
return [], out, err
|
||||
return json.loads(out), out, err
|
||||
|
|
@ -81,25 +84,20 @@ def get_volume_info(module, executable, name):
|
|||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
name=dict(type='str')
|
||||
executable=dict(type="str", default="podman"), name=dict(type="str")
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
name = module.params["name"]
|
||||
executable = module.get_bin_path(module.params["executable"], required=True)
|
||||
|
||||
inspect_results, out, err = get_volume_info(module, executable, name)
|
||||
|
||||
results = {
|
||||
"changed": False,
|
||||
"volumes": inspect_results,
|
||||
"stderr": err
|
||||
}
|
||||
results = {"changed": False, "volumes": inspect_results, "stderr": err}
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
5
setup.py
5
setup.py
|
|
@ -3,7 +3,4 @@
|
|||
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True,
|
||||
py_modules=[])
|
||||
setuptools.setup(setup_requires=["pbr"], pbr=True, py_modules=[])
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import pytest
|
||||
|
|
@ -8,12 +9,17 @@ from ansible_collections.containers.podman.plugins.module_utils.podman.common im
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('test_input, expected', [
|
||||
(["AAA", "BBB"], ["AAA", "BBB"]),
|
||||
("AAQQ", "AAQQ"),
|
||||
({"AAA": "AaaAa", "11": 22, "AbCdEf": None, "bbb": "aaaAA"},
|
||||
{"aaa": "AaaAa", "11": 22, "abcdef": None, "bbb": "aaaAA"})
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"test_input, expected",
|
||||
[
|
||||
(["AAA", "BBB"], ["AAA", "BBB"]),
|
||||
("AAQQ", "AAQQ"),
|
||||
(
|
||||
{"AAA": "AaaAa", "11": 22, "AbCdEf": None, "bbb": "aaaAA"},
|
||||
{"aaa": "AaaAa", "11": 22, "abcdef": None, "bbb": "aaaAA"},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_lower_keys(test_input, expected):
|
||||
print(lower_keys.__code__.co_filename)
|
||||
assert lower_keys(test_input) == expected
|
||||
|
|
|
|||
|
|
@ -66,15 +66,17 @@ def test_container_add_params(test_input, expected):
|
|||
[
|
||||
None, # module
|
||||
{"conmon_pidfile": "bbb"}, # module params
|
||||
{"conmonpidfile": "ccc",
|
||||
{
|
||||
"conmonpidfile": "ccc",
|
||||
"config": {
|
||||
"createcommand": [
|
||||
"podman",
|
||||
"create",
|
||||
"--conmon-pidfile=ccc",
|
||||
"testcont",
|
||||
]}
|
||||
}, # container info
|
||||
]
|
||||
},
|
||||
}, # container info
|
||||
{}, # image info
|
||||
"4.1.1", # podman version
|
||||
],
|
||||
|
|
@ -84,15 +86,17 @@ def test_container_add_params(test_input, expected):
|
|||
[
|
||||
None, # module
|
||||
{"conmon_pidfile": None}, # module params
|
||||
{"conmonpidfile": "ccc",
|
||||
{
|
||||
"conmonpidfile": "ccc",
|
||||
"config": {
|
||||
"createcommand": [
|
||||
"podman",
|
||||
"create",
|
||||
"--conmon-pidfile=ccc",
|
||||
"testcont",
|
||||
]}
|
||||
}, # container info
|
||||
]
|
||||
},
|
||||
}, # container info
|
||||
{}, # image info
|
||||
"4.1.1", # podman version
|
||||
],
|
||||
|
|
@ -102,14 +106,16 @@ def test_container_add_params(test_input, expected):
|
|||
[
|
||||
None, # module
|
||||
{"conmon_pidfile": None}, # module params
|
||||
{"conmonpidfile": None,
|
||||
{
|
||||
"conmonpidfile": None,
|
||||
"config": {
|
||||
"createcommand": [
|
||||
"podman",
|
||||
"create",
|
||||
"testcont",
|
||||
]}
|
||||
}, # container info
|
||||
]
|
||||
},
|
||||
}, # container info
|
||||
{}, # image info
|
||||
"4.1.1", # podman version
|
||||
],
|
||||
|
|
@ -118,15 +124,17 @@ def test_container_add_params(test_input, expected):
|
|||
(
|
||||
[
|
||||
None, # module
|
||||
{"conmon_pidfile": 'aaa'}, # module params
|
||||
{"conmonpidfile": None,
|
||||
{"conmon_pidfile": "aaa"}, # module params
|
||||
{
|
||||
"conmonpidfile": None,
|
||||
"config": {
|
||||
"createcommand": [
|
||||
"podman",
|
||||
"create",
|
||||
"testcont",
|
||||
]}
|
||||
}, # container info
|
||||
]
|
||||
},
|
||||
}, # container info
|
||||
{}, # image info
|
||||
"4.1.1", # podman version
|
||||
],
|
||||
|
|
@ -135,16 +143,18 @@ def test_container_add_params(test_input, expected):
|
|||
(
|
||||
[
|
||||
None, # module
|
||||
{"conmon_pidfile": 'aaa'}, # module params
|
||||
{"conmonpidfile": 'aaa',
|
||||
{"conmon_pidfile": "aaa"}, # module params
|
||||
{
|
||||
"conmonpidfile": "aaa",
|
||||
"config": {
|
||||
"createcommand": [
|
||||
"podman",
|
||||
"create",
|
||||
"--conmon-pidfile=aaa",
|
||||
"testcont",
|
||||
]}
|
||||
}, # container info
|
||||
]
|
||||
},
|
||||
}, # container info
|
||||
{}, # image info
|
||||
"4.1.1", # podman version
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue