This commit is contained in:
Luca Bilke 2024-12-12 18:03:49 +01:00
parent dd82a95cf1
commit 558192c404
Signed by: luca
GPG key ID: F6E11C9BAA7C82F5
23 changed files with 367 additions and 353 deletions

View file

@ -1,4 +0,0 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""A collection to ease the creation of docker compose files using ansible."""

View file

@ -0,0 +1,19 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
class ModuleDocFragment:
DOCUMENTATION = r"""
options:
project_name:
description:
- The name of the project.
type: str
required: true
project_dir:
description:
- The directory to place compose files in.
type: path
required: false
default: /var/lib/ez_compose
"""

View file

@ -0,0 +1,18 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
class ModuleDocFragment:
DOCUMENTATION = r"""
options:
image:
description:
- The Docker image to run.
type: str
required: true
defaults:
description:
- Defaults to be overwritten in the compose service definition
type: dict
required: false
"""

View file

@ -1,4 +0,0 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Module utilities for ez_compose modules."""

View file

@ -1,49 +1,38 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Common module utilities."""
from __future__ import annotations
import copy
from dataclasses import asdict, dataclass, field, replace
from dataclasses import dataclass, field, replace
from pathlib import Path
from typing import Any, Callable
from typing import TYPE_CHECKING, Any
import yaml
from ansible.module_utils.basic import AnsibleModule
PROJECTS_DIR = "/var/lib/ez_compose"
if TYPE_CHECKING:
from ansible.module_utils.basic import AnsibleModule # type: ignore[reportMissingStubFile]
BASE_ARGS = {
"project_name": {
"type": "str",
"required": True,
},
"name": {
"type": "str",
"required": True,
},
"project_dir": {
"type": "path",
"default": "/var/lib/ez_compose",
},
}
@dataclass(frozen=True)
class Result:
"""Module result object."""
changed: bool = False
diff: dict[str, Any] = field(default_factory=dict)
# @dataclass(frozen=True)
# class Settings:
# projects_dir: str = "/usr/local/share/ez_compose/"
@dataclass(frozen=True)
class State:
"""Execution state object."""
module: AnsibleModule
result: Result
compose_filepath: str
@ -55,7 +44,6 @@ def recursive_update(
default: dict[Any, Any],
update: dict[Any, Any],
) -> dict[Any, Any]:
"""Recursively update a dictionary."""
for key in update: # noqa: PLC0206
if isinstance(update[key], dict) and isinstance(default.get(key), dict):
default[key] = recursive_update(default[key], update[key])
@ -73,14 +61,11 @@ def recursive_update(
def get_state(module: AnsibleModule) -> State:
"""Create a new state object, loading the compose file into "before" if it exists."""
compose_filepath = (
f"{PROJECTS_DIR}/{module.params['project_name']}/docker-compose.yml"
)
compose_filepath = f"{module.params['project_dir']}/{module.params['name']}/docker-compose.yml"
try:
with Path(compose_filepath).open("r") as fp:
before = yaml.safe_load(fp)
except FileNotFoundError:
before: dict[str, Any] = {}
@ -89,9 +74,12 @@ def get_state(module: AnsibleModule) -> State:
result=Result(),
compose_filepath=compose_filepath,
before=before,
after={},
after={
"name": module.params["name"],
},
)
def update_project(state: State) -> State:
"""Ensure that networks/volumes that exist in services also exist in the project."""
project = copy.deepcopy(state.after)
@ -124,30 +112,9 @@ def update_project(state: State) -> State:
def write_compose(state: State) -> State:
"""Write the compose file to disk."""
file = state.compose_filepath
with Path(file).open(mode="w") as stream:
yaml.dump(state.after, stream)
return state
def run_module(
args: dict[str, Any] | None = None,
execute: Callable[[State], State] = lambda _: _,
) -> None:
"""Handle module setup and teardown."""
module = AnsibleModule(
argument_spec={**BASE_ARGS, **(args or {})},
supports_check_mode=True,
)
state = get_state(module)
state = execute(state)
if not state.module.check_mode:
write_compose(state)
state.module.exit_json(**asdict(state.result)) # type: ignore[reportUnkownMemberType]

View file

@ -1,4 +1,18 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Module utilities for ez_compose label modules."""
__all__ = [
"common",
"docker_volume_backupper",
"traefik_middleware",
"traefik_router",
"traefik_service",
]
from . import (
common,
docker_volume_backupper,
traefik_middleware,
traefik_router,
traefik_service,
)

View file

@ -1,21 +1,23 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Label module utilities."""
from __future__ import annotations
from typing import Any, Callable
from typing import TYPE_CHECKING, Callable
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
run_module,
)
if TYPE_CHECKING:
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
def run(
extra_args: dict[str, Any] | None = None,
BASE_ARGS = {
"name": {"type": "str", "required": True},
}
def run_helper(
state: State,
helper: Callable[[State], State] = lambda _: _,
) -> None:
"""Wrap module execution function for label helpers."""
run_module(extra_args or {}, helper)
) -> State:
return helper(state)

View file

@ -1,21 +1,23 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Docker Volume Backup label helper."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label
import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service
import ansible_collections.ssnailed.ez_compose.plugins.module_utils.service.common as service # type: ignore[reportMissingTypeStubs]
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {
"stop": {"type": "bool"},
}
def helper(state: State) -> State:
"""Add labels to the service to stop it during backup."""
stop = state.module.params.get("stop", True)
project_name = state.module.params["project_name"]
@ -27,11 +29,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def run() -> None:
"""Run the backupper label helper."""
extra_args = {
"stop": {"type": "bool"},
}
label.run(extra_args, helper)

View file

@ -5,11 +5,19 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label
import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service
import ansible_collections.ssnailed.ez_compose.plugins.module_utils.service.common as service # type: ignore[reportMissingTypeStubs]
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {
"proxy_type": {"type": "str"},
"middleware_name": {"type": "string"},
"middleware": {"type": "str", "required": True},
"settings": {"type": "list", "required": True},
}
def helper(state: State) -> State:
@ -32,17 +40,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def main():
extra_args = {
"proxy_type": {"type": "str"},
"middleware_name": {"type": "string"},
"middleware": {"type": "str", "required": True},
"settings": {"type": "list", "required": True},
}
label.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -5,11 +5,22 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label
import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service
import ansible_collections.ssnailed.ez_compose.plugins.module_utils.service.common as service # type: ignore[reportMissingTypeStubs]
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {
"proxy_type": {"type": "str"},
"router_name": {"type": "str"},
"rule": {"type": "str", "required": True},
"service": {"type": "str"},
"certresolver": {"type": "str"},
"entrypoints": {"type": "list"},
"middlewares": {"type": "list"},
}
def helper(state: State) -> State:
@ -50,22 +61,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def main():
extra_args = {
"proxy_type": {"type": "str"},
"router_name": {
"type": "str",
},
"rule": {"type": "str", "required": True},
"service": {"type": "str"},
"certresolver": {"type": "str"},
"entrypoints": {"type": "list"},
"middlewares": {"type": "list"},
}
label.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -3,16 +3,20 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label
import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service
import ansible_collections.ssnailed.ez_compose.plugins.module_utils.service.common as service # type: ignore[reportMissingTypeStubs]
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
from typing import Any
EXTRA_ARGS = {
"proxy_type": {"type": "str"},
"service_name": {"type": "string"},
"port": {"type": "int"},
}
def helper(state: State) -> State:
@ -37,16 +41,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def main():
extra_args = {
"proxy_type": {"type": "str"},
"service_name": {"type": "string"},
"port": {"type": "int"},
}
label.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -1,4 +1,21 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
__all__ = [
"common",
"custom",
"docker_in_docker",
"docker_socket_proxy",
"docker_volume_backupper",
"mariadb",
"postgres",
"redis",
]
"""Module utilities for ez_compose service modules."""
from . import (
common,
custom,
docker_in_docker,
docker_socket_proxy,
docker_volume_backupper,
mariadb,
postgres,
redis,
)

View file

@ -1,65 +1,51 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Service module utilities."""
from __future__ import annotations
import copy
from dataclasses import replace
from typing import Any, Callable
from typing import TYPE_CHECKING, Any, Callable
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
recursive_update,
run_module,
update_project,
)
BASE_SERVICE_ARGS = {
"project_name": {
"type": "str",
"required": True,
},
"name": {
"type": "str",
"required": True,
},
"image": {
"type": "str",
},
"defaults": {
"type": "dict",
},
if TYPE_CHECKING:
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
BASE_ARGS = {
"name": {"type": "str", "required": True},
"image": {"type": "str", "required": True},
"defaults": {"type": "dict"},
}
def apply_base(state: State) -> State:
"""Apply base service configuration."""
service_name = state.module.params["name"]
project_name = state.module.params["project_name"]
image = state.module.params["image"]
def apply_base(state: State, params: dict[str, Any]) -> State:
project_name = state.module.params["name"]
new: dict[str, Any] = {
"service_name": f"{project_name}_{service_name}",
"hostname": f"{project_name}_{service_name}",
"image": image,
"container_name": f"{project_name}_{params["name"]}",
"hostname": f"{project_name}_{params["name"]}",
"image": params["image"],
"restart": "unless-stopped",
"environment": {},
"labels": {},
"volumes": [],
"networks": {
f"{project_name}_internal": None,
"internal": None,
},
}
return update(state, new)
def set_defaults(state: State) -> State:
"""Set default values for a service."""
container_name = state.module.params["name"]
defaults = state.module.params["defaults"]
def set_defaults(state: State, params: dict[str, Any]) -> State:
container_name = params["name"]
defaults = params.get("defaults", {})
project = copy.deepcopy(state.after)
services = project["services"]
service = services[container_name]
@ -72,7 +58,6 @@ def set_defaults(state: State) -> State:
def update(state: State, update: dict[str, Any]) -> State:
"""Update a service with the given dictionary."""
project = copy.deepcopy(state.after)
service_name = state.module.params["name"]
@ -81,14 +66,12 @@ def update(state: State, update: dict[str, Any]) -> State:
return replace(state, after=project)
def run(
extra_args: dict[str, Any] | None = None,
helper: Callable[[State], State] = lambda _: _,
) -> None:
"""Wrap module execution function for service helpers."""
def execute(state: State) -> State:
for f in [apply_base, set_defaults, helper, update_project]:
state = f(state)
return state
run_module({**BASE_SERVICE_ARGS, **(extra_args or {})}, execute)
def run_helper(
state: State,
params: dict[str, Any],
helper: Callable[[State, dict[str, Any]], State] = lambda x, _: x,
) -> State:
state = apply_base(state, params)
state = set_defaults(state, params)
state = helper(state, params)
return update_project(state)

View file

@ -4,70 +4,34 @@
from __future__ import annotations
import copy
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
finish,
get_state,
update_project,
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.service import ( # type: ignore[reportMissingTypeStubs]
common as service,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
def helper(state: State) -> State:
project_name = state.module.params["project_name"]
definition = state.module.params["definition"]
internal_network = state.module.params.get("internal_network", False)
EXTRA_ARGS = {
"internal_network": {"type": "bool", "default": False},
"definition": {"type": "dict", "required": True},
}
def helper(state: State, params: dict[str, Any]) -> State:
definition = params["definition"]
internal_network = params["internal_network"]
update = copy.deepcopy(definition)
networks = update.get("networks", {})
if internal_network:
networks[f"{project_name}_internal"] = None
networks["internal"] = None
update["networks"] = networks
return service.update(state, update)
def main():
module = AnsibleModule(
argument_spec={
"project_name": {
"type": "str",
"required": True,
},
"name": {
"type": "str",
"required": True,
},
"internal_network": {
"type": "bool",
},
"state": {
"type": "str",
"default": "present",
"choices": ["present", "absent"],
},
"definition": {
"type": "dict",
"required": True,
},
},
supports_check_mode=True,
)
state = get_state(module)
for f in [helper, update_project]:
state = f(state)
finish(state)
if __name__ == "__main__":
main()

View file

@ -3,15 +3,24 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.service import ( # type: ignore[reportMissingTypeStubs]
common as service,
)
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
def helper(state: State) -> State:
command = state.module.params.get("command")
EXTRA_ARGS = {
"command": {"type": "list"},
}
def helper(state: State, params: dict[str, Any]) -> State:
command = params.get("command")
update = {
"privileged": True,
@ -21,14 +30,3 @@ def helper(state: State) -> State:
update["command"] = command
return service.update(state, update)
def main():
extra_args = {
"command": {"type": "list"},
}
service.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -3,16 +3,24 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.service import ( # type: ignore[reportMissingTypeStubs]
common as service,
)
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {
"read_only": {"type": "bool"},
}
def helper(state: State) -> State:
read_only = state.module.params.get("read_only", True)
def helper(state: State, params: dict[str, Any]) -> State:
read_only = params.get("read_only", True)
volumes = [
{
@ -20,7 +28,7 @@ def helper(state: State) -> State:
"source": "/var/run/docker.sock",
"target": "/var/run/docker.sock",
"read_only": read_only,
}
},
]
update = {
@ -28,14 +36,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def main():
extra_args = {
"read_only": {"type": "bool"},
}
service.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -3,19 +3,28 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.service import ( # type: ignore[reportMissingTypeStubs]
common as service,
)
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {
"archive": {"type": "str"},
"backup_volumes": {"type": "list"},
}
def helper(state: State) -> State:
archive = state.module.params.get("archive")
backup_volumes = state.module.params.get("backup_volumes", [])
service_name = state.module.params["name"]
project_name = state.module.params["project_name"]
def helper(state: State, params: dict[str, Any]) -> State:
archive = params.get("archive")
backup_volumes = params.get("backup_volumes", [])
service_name = params["name"]
project_name = params["project_name"]
volumes = [
{
@ -43,7 +52,7 @@ def helper(state: State) -> State:
"source": f"{archive}/{project_name}",
"target": "/archive",
"bind": {"create_host_path": True},
}
},
)
update = {
@ -52,15 +61,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def main():
extra_args = {
"archive": {"type": "str"},
"backup_volumes": {"type": "list"},
}
service.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -4,22 +4,34 @@
from __future__ import annotations
import shlex
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.service import ( # type: ignore[reportMissingTypeStubs]
common as service,
)
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {
"archive": {"type": "bool"},
"database": {"type": "str", "required": True},
"username": {"type": "str", "required": True},
"password": {"type": "str", "required": True},
"root_password": {"type": "str"},
}
def helper(state: State) -> State:
backup = state.module.params.get("backup", True)
database = state.module.params["database"]
username = state.module.params["username"]
password = state.module.params["password"]
root_password = state.module.params["root_password"]
service_name = state.module.params["name"]
project_name = state.module.params["project_name"]
def helper(state: State, params: dict[str, Any]) -> State:
backup = params.get("backup", True)
database = params["database"]
username = params["username"]
password = params["password"]
root_password = params["root_password"]
service_name = params["name"]
project_name = params["project_name"]
volumes = [
{
@ -73,18 +85,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def main():
extra_args = {
"archive": {"type": "bool"},
"database": {"type": "str", "required": True},
"username": {"type": "str", "required": True},
"password": {"type": "str", "required": True},
"root_password": {"type": "str"},
}
service.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -4,21 +4,32 @@
from __future__ import annotations
import shlex
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.service import ( # type: ignore[reportMissingTypeStubs]
common as service,
)
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {
"archive": {"type": "bool"},
"database": {"type": "str", "required": True},
"username": {"type": "str", "required": True},
"password": {"type": "str", "required": True},
}
def helper(state: State) -> State:
backup = state.module.params.get("backup", True)
database = state.module.params["database"]
username = state.module.params["username"]
password = state.module.params["password"]
service_name = state.module.params["name"]
project_name = state.module.params["project_name"]
def helper(state: State, params: dict[str, Any]) -> State:
backup = params.get("backup", True)
database = params["database"]
username = params["username"]
password = params["password"]
service_name = params["name"]
project_name = params["project_name"]
volumes = [
{
@ -65,17 +76,3 @@ def helper(state: State) -> State:
}
return service.update(state, update)
def main():
extra_args = {
"archive": {"type": "bool"},
"database": {"type": "str", "required": True},
"username": {"type": "str", "required": True},
"password": {"type": "str", "required": True},
}
service.run(extra_args, helper)
if __name__ == "__main__":
main()

View file

@ -3,12 +3,15 @@
from __future__ import annotations
from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ansible_collections.ssnailed.ez_compose.plugins.module_utils.common import ( # type: ignore[reportMissingTypeStubs]
State,
)
EXTRA_ARGS = {}
def main():
service.run()
if __name__ == "__main__":
main()
def helper(state: State) -> State:
return state

View file

@ -0,0 +1,79 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import Any
from ansible.module_utils.basic import AnsibleModule # type: ignore[reportMissingStubFile]
from ansible_collections.ssnailed.ez_compose.plugins.module_utils import ( # type: ignore[reportMissingStubFile]
common,
label,
service,
)
def label_argument_spec() -> dict[str, Any]:
label_args: dict[str, Any] = {
"type": "dict",
"options": {},
}
for module in label.__all__:
if module == "common":
continue
options = {
**label.common.BASE_ARGS,
**getattr(label, module).EXTRA_ARGS,
}
label_args["options"][module] = {
"type": "dict",
"options": options,
}
return label_args
def service_argument_spec() -> dict[str, Any]:
service_args: dict[str, Any] = {
"type": "dict",
"options": {},
"required": True,
}
for module in service.__all__:
if module == "common":
continue
options = {
**service.common.BASE_ARGS,
**getattr(service, module).EXTRA_ARGS,
}
if module == "custom":
options["label_helpers"] = label_argument_spec()
service_args["options"][module] = {
"type": "list",
"elements": "dict",
"options": options,
}
return service_args
def main() -> None:
module = AnsibleModule(
argument_spec={
**common.BASE_ARGS,
"services": service_argument_spec(),
},
)
state = common.get_state(module)
if __name__ == "__main__":
main()

View file

@ -5,7 +5,7 @@ output-format = "grouped"
[tool.ruff.lint]
select = ["ALL"]
ignore = ["ERA001", "TD002", "TD003"]
ignore = ["ERA001", "TD002", "TD003", "INP001", "D100", "D101", "D103", "D104"]
[tool.ruff.format]
line-ending = "lf"

View file

@ -1,2 +1 @@
ansible-core==2.17.*
pydantic==2.9.*