Skip to content

Commit

Permalink
WIP add Quadlet support for Podman
Browse files Browse the repository at this point in the history
Fix containers#671
Signed-off-by: Sagi Shnaidman <sshnaidm@redhat.com>
  • Loading branch information
sshnaidm committed Feb 29, 2024
1 parent f2dcda6 commit 308c7ff
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 1 deletion.
16 changes: 15 additions & 1 deletion plugins/module_utils/podman/podman_container_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
from ansible_collections.containers.podman.plugins.module_utils.podman.common import delete_systemd
from ansible_collections.containers.podman.plugins.module_utils.podman.common import normalize_signal
from ansible_collections.containers.podman.plugins.module_utils.podman.common import ARGUMENTS_OPTS_DICT
from .quadlet import ContainerQuadlet

__metaclass__ = type

ARGUMENTS_SPEC_CONTAINER = dict(
name=dict(required=True, type='str'),
executable=dict(default='podman', type='str'),
state=dict(type='str', default='started', choices=[
'absent', 'present', 'stopped', 'started', 'created']),
'absent', 'present', 'stopped', 'started', 'created', 'quadlet']),
image=dict(type='str'),
annotation=dict(type='dict'),
attach=dict(type='list', elements='str', choices=['stdout', 'stderr', 'stdin']),
Expand Down Expand Up @@ -116,6 +117,7 @@
publish=dict(type='list', elements='str', aliases=[
'ports', 'published', 'published_ports']),
publish_all=dict(type='bool'),
quadlet_file_path=dict(type='str'),
read_only=dict(type='bool'),
read_only_tmpfs=dict(type='bool'),
recreate=dict(type='bool', default=False),
Expand Down Expand Up @@ -1800,6 +1802,17 @@ def make_absent(self):
self.results.update({'container': {},
'podman_actions': self.container.actions})

def make_quadlet(self):
quadlet_file_path = self.module_params['quadlet_file_path']
if not quadlet_file_path:
self.module.fail_json(msg="quadlet_file_path is required for quadlet state")
if not os.path.exists(os.path.dirname(quadlet_file_path)):
self.module.fail_json(msg="Directory for quadlet_file_path doesn't exist")
quadlet = ContainerQuadlet(self.module_params)
quadlet.write_to_file(quadlet_file_path)



def execute(self):
"""Execute the desired action according to map of actions & states."""
states_map = {
Expand All @@ -1808,6 +1821,7 @@ def execute(self):
'absent': self.make_absent,
'stopped': self.make_stopped,
'created': self.make_created,
'quadlet': self.make_quadlet,
}
process_action = states_map[self.state]
process_action()
Expand Down
155 changes: 155 additions & 0 deletions plugins/module_utils/podman/quadlet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Copyright (c) 2024 Sagi Shnaidman
# 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


class Quadlet:
param_map = {}

def __init__(self, section: str, params: dict):
self.section = section
self.custom_params = self.custom_prepare_params(params)
self.dict_params = self.prepare_params()

def custom_prepare_params(self, params: dict) -> dict:
"""
Custom parameter processing for specific Quadlet types.
"""
# This should be implemented in child classes if needed.
return params

def prepare_params(self) -> dict:
"""
Convert parameter values as per param_map using a dictionary comprehension.
"""
processed_params = []
for param_key, quadlet_key in self.param_map.items():
value = self.custom_params.get(param_key)
if value is not None:
if isinstance(value, list):
# Add an entry for each item in the list
for item in value:
processed_params.append([quadlet_key, item])
else:
if isinstance(value, bool):
value = str(value).lower()
# Add a single entry for the key
processed_params.append([quadlet_key, value])
return processed_params

def create_quadlet_content(self) -> str:
"""
Construct the quadlet content as a string.
"""
return f"[{self.section}]\n" + "\n".join(
f"{key}={value}" for key, value in self.dict_params
) + "\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:
file.write(content)
print(f"{self.section} quadlet file written to {path}")


class ContainerQuadlet(Quadlet):
param_map = {
'annotation': 'Annotation',
'name': 'ContainerName',
'image': 'Image',
'volume': 'Volume',
'network': 'Network',
'command': 'Exec',
'cap_add': 'AddCapability',
# Add more parameter mappings specific to containers
}

def __init__(self, params: dict):
super().__init__("Container", params)

def custom_prepare_params(self, params: dict) -> dict:
"""
Custom parameter processing for container-specific parameters.
"""
if params["annotation"]:
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["command"]:
params["command"] = (" ".join(params["command"])
if isinstance(params["command"], list)
else params["command"])
# Return params with custom processing applied
return params


class NetworkQuadlet(Quadlet):
param_map = {
'name': 'NetworkName',
'internal': 'Internal',
'driver': 'Driver',
'gateway': 'Gateway',
'disable_dns': 'DisableDNS',
'subnet': 'Subnet',
# Add more parameter mappings specific to networks
}

def __init__(self, params: dict):
super().__init__("Network", params)


# This is a inherited class that represents a Quadlet file for the Podman image
class Image(Quadlet):
pass


# This is a inherited class that represents a Quadlet file for the Podman pod
class Pod(Quadlet):
pass


# This is a inherited class that represents a Quadlet file for the Podman volume
class Volume(Quadlet):
pass


# This is a inherited class that represents a Quadlet file for the Podman kube
class Kube(Quadlet):
pass


if __name__ == "__main__":
container_params = {
"annotation": {"key1": "value1", "key2": "value2"},
"name": "my_container",
"command": ["sleep", "10"],
"image": "quay.io/aaa/alpine",
"volume": ["/my/host/path:/my/container/path:ro", "/another/host/path:/another/container/path:rw"],
"cap_add": ["CAP_DAC_OVERRIDE", "CAP_IPC_OWNER"],
"network": None,
"foo": "bar",

# ... other container-specific parameters
}

container_quadlet = ContainerQuadlet(container_params)
print(container_quadlet.create_quadlet_content())
print()

network_params = {
"name": "my_network",
"internal": True,
"driver": "bridge",
"gateway": "1.2.3.4",
"disable_dns": False,
"subnet": None,
# ... other network-specific parameters
}
network_quadlet = NetworkQuadlet(network_params)
print(network_quadlet.create_quadlet_content())

0 comments on commit 308c7ff

Please sign in to comment.