Compare commits

...

4 commits

Author SHA1 Message Date
7b8f646293 cleanup 2025-01-18 19:19:17 +01:00
c78e240884 WIP 2025-01-18 19:16:11 +01:00
77e32d4543 WIP 2025-01-17 18:31:17 +01:00
c2ceb239a3 WIP 2025-01-13 20:52:52 +01:00
22 changed files with 271 additions and 212 deletions

31
.lazy.lua Normal file
View file

@ -0,0 +1,31 @@
local function find_table_with_value(tables, search_value)
for _, tbl in ipairs(tables) do
for _, value in pairs(tbl) do
if value == search_value then
return tbl
end
end
end
return nil
end
return {
{
"mfussenegger/nvim-dap",
optional = true,
config = function()
local root = vim.lsp.get_clients()[1].root_dir
table.insert(require("dap").configurations.python, {
type = "python",
request = "launch",
name = "custom",
program = root .. "/plugins/modules/compose.py",
console = "integratedTerminal",
pythonPath = os.getenv("VIRTUAL_ENV") .. "/bin/python",
args = { root .. "/test.json" },
justMyCode = false,
})
find_table_with_value(require("lazyvim.plugins.extras.dap.core"), "mfussenegger/nvim-dap").config()
end,
},
}

View file

@ -1,4 +1,4 @@
Copyright 2024 Luca Bilke
Copyright 2025 Luca Bilke
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
@ -6,7 +6,7 @@ from __future__ import annotations
import copy
from dataclasses import replace
from pathlib import Path
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, cast
import yaml
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import Result, State
@ -15,23 +15,32 @@ if TYPE_CHECKING:
from ansible.module_utils.basic import AnsibleModule # pyright: ignore[reportMissingTypeStubs]
def clean_none(obj: dict[str, Any]) -> dict[str, Any]:
obj = copy.deepcopy(obj)
for k in copy.deepcopy(obj):
if obj.get(k, "sentinel") is None:
del obj[k]
return obj
def recursive_update(
default: dict[Any, Any],
update: dict[Any, Any],
) -> dict[Any, Any]:
default: dict[str, Any],
update: dict[str, Any],
) -> dict[str, Any]:
default = copy.deepcopy(default)
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])
for k, v in update.items():
if isinstance(v, dict):
v = cast(dict[str, Any], v)
default[k] = recursive_update(default.get(k) or {}, v)
elif isinstance(update[key], list) and isinstance(default.get(key), list):
default_set = set(default[key])
custom_set = set(update[key])
default[key] = list(default_set.union(custom_set))
elif isinstance(v, list):
v = cast(list[Any], v)
new = cast(list[Any], (default.get(k) or []))
new.extend(v)
default[k] = new
else:
default[key] = update[key]
default[k] = v
return default
@ -91,6 +100,49 @@ def update_project(state: State) -> State:
return replace(state, after=project)
def set_result(state: State) -> State: # noqa: C901
def _changed(before: Any, after: Any) -> bool: # noqa: ANN401, C901, PLR0911
if type(before) is not type(after):
return True
if isinstance(before, dict):
before = cast(dict[str, Any], before)
if len(before) != len(after):
return True
for key in before:
if key not in after:
return True
if _changed(before[key], after[key]):
return True
return False
if isinstance(before, list):
before = sorted(cast(list[Any], before))
after = sorted(after)
if len(before) != len(after):
return True
for index in before.enumerate():
if _changed(before[index], after[index]):
return True
return before != after
result = Result(
_changed(state.before, state.after),
{
"before": state.before,
"after": state.after,
},
)
return replace(state, result=result)
def write_compose(state: State) -> State:
file = state.compose_filepath

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
__all__ = [

View file

@ -1,23 +1,42 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Callable
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
clean_none,
recursive_update,
)
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
BASE_ARGS: dict[str, Any] = {}
def apply_update(
service: dict[str, Any],
update: dict[str, Any],
) -> dict[str, Any]:
return recursive_update(service, update)
def get_default_args(state: State, helper_name: str) -> dict[str, Any]:
settings: dict[str, Any] = state.module.params.get("settings") or {}
label_default_args: dict[str, Any] = settings.get("label_default_args") or {}
default_args: dict[str, Any] = label_default_args.get(helper_name) or {}
return clean_none(default_args)
def run_helper(
state: State,
service_name: str,
service: dict[str, Any],
params: dict[str, Any],
helper: Callable[[State, str, dict[str, Any]], State] = lambda a, _b, _c: a,
) -> State:
return helper(state, service_name, params)
helper: Callable[[State, str, dict[str, Any]], dict[str, Any]] = lambda _a, _b, _c: {},
) -> dict[str, Any]:
update = helper(state, service_name, params)
return apply_update(service, update)

View file

@ -1,23 +1,19 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"stop": {"type": "bool", "default": True},
}
def helper(state: State, service_name: str, params: dict[str, Any]) -> State:
def helper(state: State, _service_name: str, params: dict[str, Any]) -> dict[str, Any]:
stop: bool = params["stop"]
project_name: str = state.module.params["name"]
@ -28,4 +24,4 @@ def helper(state: State, service_name: str, params: dict[str, Any]) -> State:
"docker-volume-backup.stop-during-backup": project_name,
}
return service.common.update(state, {"name": service_name}, update)
return update

View file

@ -1,16 +1,12 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"proxy_type": {"type": "str", "default": "http"},
@ -20,22 +16,19 @@ EXTRA_ARGS = {
}
def helper(state: State, service_name: str, params: dict[str, Any]) -> State:
def helper(state: State, service_name: str, params: dict[str, Any]) -> dict[str, Any]:
project_name: str = state.module.params["name"]
middleware: str = params["middleware"]
settings: dict[str, str] = params["settings"]
proxy_type: str = state.module.params["proxy_type"]
proxy_type: str = params["proxy_type"]
name: str = (
params.get("name")
or f"{project_name}_{service_name}_{proxy_type}_{middleware.lower()}"
params.get("name") or f"{project_name}_{service_name}_{proxy_type}_{middleware.lower()}"
)
prefix = f"traefik.{proxy_type}.middlewares.{name}"
labels = {f"{prefix}.{middleware}.{key}": var for key, var in settings.items()}
update = {
return {
"labels": labels,
}
return service.common.update(state, {"name": service_name}, update)

View file

@ -1,16 +1,12 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"name": {"type": "str"},
@ -23,7 +19,7 @@ EXTRA_ARGS = {
}
def helper(state: State, service_name: str, params: dict[str, Any]) -> State:
def helper(state: State, service_name: str, params: dict[str, Any]) -> dict[str, Any]:
project_name: str = state.module.params["name"]
rule: str = params["rule"]
traefik_service: str | None = params.get("service")
@ -52,8 +48,6 @@ def helper(state: State, service_name: str, params: dict[str, Any]) -> State:
if middlewares:
labels[f"{prefix}.middlewares"] = ",".join(middlewares)
update = {
return {
"labels": labels,
}
return service.common.update(state, {"name": service_name}, update)

View file

@ -1,16 +1,12 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"proxy_type": {"type": "str", "default": "http"},
@ -19,7 +15,7 @@ EXTRA_ARGS = {
}
def helper(state: State, service_name: str, params: dict[str, Any]) -> State:
def helper(state: State, service_name: str, params: dict[str, Any]) -> dict[str, Any]:
project_name: str = state.module.params["name"]
port: int | None = params.get("port")
proxy_type: str = params["proxy_type"]
@ -32,8 +28,6 @@ def helper(state: State, service_name: str, params: dict[str, Any]) -> State:
if port:
labels[f"{prefix}.loadbalancer.server.port"] = str(port)
update = {
return {
"labels": labels,
}
return service.common.update(state, {"name": service_name}, update)

View file

@ -1,9 +1,9 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
@ -13,7 +13,7 @@ if TYPE_CHECKING:
@dataclass(frozen=True)
class Result:
changed: bool = False
diff: dict[str, Any] = field(default_factory=dict)
diff: dict[str, Any] | None = None
@dataclass(frozen=True)

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
__all__ = [

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
@ -8,14 +8,12 @@ from dataclasses import replace
from typing import TYPE_CHECKING, Any, Callable
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
clean_none,
recursive_update,
update_project,
)
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
BASE_ARGS: dict[str, Any] = {
"name": {"type": "str"},
@ -23,85 +21,70 @@ BASE_ARGS: dict[str, Any] = {
}
def apply_base(state: State, params: dict[str, Any]) -> State:
def get_base_definition(state: State, service_name: str) -> dict[str, Any]:
project_name: str = state.module.params["name"]
new: dict[str, Any] = {
"container_name": f"{project_name}_{params['name']}",
"hostname": f"{project_name}_{params['name']}",
return {
"container_name": f"{project_name}_{service_name}",
"hostname": f"{project_name}_{service_name}",
"restart": "unless-stopped",
"environment": {},
"labels": {},
"volumes": [],
"networks": {
"internal": None,
},
} | (
state.module.params.get("settings", {})
.get("default_definition", {})
.get(params["name"], {})
)
return update(state, params, new)
# TODO: this should be set per service helper
# "networks": {
# "internal": None,
# },
}
def apply_definition(state: State, params: dict[str, Any], definition: dict[str, Any]) -> State:
service_name: str = params["name"]
def get_default_definition(state: State, service_name: str) -> dict[str, Any]:
settings: dict[str, Any] = state.module.params.get("settings") or {}
default_definition: dict[str, Any] = settings.get("default_definition") or {}
service_default_definitions: dict[str, Any] = settings.get("service_default_definitions") or {}
service_default_definition: dict[str, Any] = service_default_definitions.get(service_name) or {}
return default_definition | service_default_definition
def get_default_args(state: State, helper_name: str) -> dict[str, Any]:
settings: dict[str, Any] = state.module.params.get("settings") or {}
service_default_args: dict[str, Any] = settings.get("service_default_args") or {}
default_args: dict[str, Any] = service_default_args.get(helper_name) or {}
return clean_none(default_args)
def apply_update(state: State, service_name: str, update: dict[str, Any]) -> State:
project = copy.deepcopy(state.after)
services: dict[str, Any] = project["services"]
service: dict[str, Any] = services[service_name]
service = project["services"].get(service_name, {})
service = recursive_update(service, update)
_ = recursive_update(service, definition)
volumes: list[dict[str, Any]] = service.get("volumes") or []
unique_volumes = list({vol["source"]: vol for vol in volumes if "target" in vol}.values())
service["volumes"] = unique_volumes
services.update({service_name: service})
project["services"][service_name] = service
return replace(state, after=project)
def apply_settings(state: State, params: dict[str, Any]) -> State:
settings = state.module.params.get("settings", {})
params = settings.get("service_default_args", {}).get(params["name"], {}) | params
return update(
state,
params,
settings.get("service_default_definitions", {}).get(params["name"], {}),
)
def update(state: State, params: dict[str, Any], update: dict[str, Any]) -> State:
service_name: str = params["name"]
project = copy.deepcopy(state.after)
project["services"][service_name] = project["services"].get(service_name, {})
_ = recursive_update(project["services"][service_name], update)
# FIX: this silently throws out misconfigured volumes
unique_volumes = dict(
{
vol["target"]: vol
for vol in project["services"][service_name].get("volumes", [])
if "target" in vol
}.values(),
)
project["services"][service_name]["volumes"] = unique_volumes
return replace(state, after=project)
def run_helper(
state: State,
params: dict[str, Any],
helper: Callable[[State, dict[str, Any]], State] = lambda x, _: x,
helper: Callable[[State, dict[str, Any]], dict[str, Any]] = lambda _a, _b: {},
) -> State:
if not params.get("name"):
params["name"] = str.split(helper.__module__, ".")[-1]
if not params.get("overwrite"):
params["overwrite"] = params.get("definition", {})
base_definition = get_base_definition(state, params["name"])
state = apply_update(state, params["name"], base_definition)
state = apply_base(state, params)
state = apply_settings(state, params)
state = helper(state, params)
state = apply_definition(state, params, params["overwrite"])
return update_project(state)
default_definition = get_default_definition(state, params["name"])
state = apply_update(state, params["name"], default_definition)
helper_update = helper(state, params)
state = apply_update(state, params["name"], helper_update)
if not (overwrite := params.get("overwrite")):
overwrite = params.get("definition", {})
return apply_update(state, params["name"], overwrite)

View file

@ -1,11 +1,12 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import label, service, spec
from ansible_collections.snailed.ez_compose.plugins.module_utils import label, spec
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import clean_none
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
@ -20,20 +21,19 @@ FORCE_ARGS = {
}
def helper(state: State, params: dict[str, Any]) -> State:
def helper(state: State, params: dict[str, Any]) -> dict[str, Any]:
internal_network: bool = params["internal_network"]
update: dict[str, Any] = {}
networks = update.get("networks", {})
if internal_network:
networks = update.get("networks", {})
networks["internal"] = None
update["networks"] = networks
update["networks"] = networks
for name, args in [(x, y) for x, y in params.get("label_helpers", {}).items() if y]:
label_params = label.common.get_default_args(state, name) | clean_none(args)
helper = getattr(label, name).helper
state = label.common.run_helper(state, params["name"], args, helper)
update = label.common.run_helper(state, params["name"], update, label_params, helper)
return service.common.update(state, params, update)
return update

View file

@ -1,23 +1,17 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {}
def helper(state: State, params: dict[str, Any]) -> State:
update = {
def helper(_state: State, _params: dict[str, Any]) -> dict[str, Any]:
return {
"privileged": True,
}
return service.common.update(state, params, update)

View file

@ -1,23 +1,19 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"read_only": {"type": "bool", "default": True},
}
def helper(state: State, params: dict[str, Any]) -> State:
def helper(_state: State, params: dict[str, Any]) -> dict[str, Any]:
read_only = params["read_only"]
volumes = [
@ -29,8 +25,6 @@ def helper(state: State, params: dict[str, Any]) -> State:
},
]
update = {
return {
"volumes": volumes,
}
return service.common.update(state, params, update)

View file

@ -1,16 +1,12 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"archive": {"type": "path"},
@ -18,7 +14,7 @@ EXTRA_ARGS = {
}
def helper(state: State, params: dict[str, Any]) -> State:
def helper(state: State, params: dict[str, Any]) -> dict[str, Any]:
project_name: str = state.module.params["name"]
archive: str | None = params.get("archive")
backup_volumes: list[str] | None = params["backup_volumes"]
@ -53,9 +49,7 @@ def helper(state: State, params: dict[str, Any]) -> State:
},
)
update = {
return {
"environment": environment,
"volumes": volumes,
}
return service.common.update(state, params, update)

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
@ -6,12 +6,8 @@ from __future__ import annotations
import shlex
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"backup": {"type": "bool", "default": True},
@ -22,7 +18,7 @@ EXTRA_ARGS = {
}
def helper(state: State, params: dict[str, Any]) -> State:
def helper(state: State, params: dict[str, Any]) -> dict[str, Any]:
project_name: str = state.module.params["name"]
backup: bool = params["backup"]
database: str = params["database"]
@ -76,10 +72,8 @@ def helper(state: State, params: dict[str, Any]) -> State:
},
)
update = {
return {
"environment": environment,
"volumes": volumes,
"labels": labels,
}
return service.common.update(state, params, update)

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
@ -6,12 +6,8 @@ from __future__ import annotations
import shlex
from typing import TYPE_CHECKING, Any
from ansible_collections.snailed.ez_compose.plugins.module_utils import service
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {
"backup": {"type": "bool", "default": True},
@ -21,7 +17,7 @@ EXTRA_ARGS = {
}
def helper(state: State, params: dict[str, Any]) -> State:
def helper(state: State, params: dict[str, Any]) -> dict[str, Any]:
project_name: str = state.module.params["name"]
backup: bool = params["backup"]
database: str = params["database"]
@ -67,10 +63,8 @@ def helper(state: State, params: dict[str, Any]) -> State:
},
)
update = {
return {
"environment": environment,
"volumes": volumes,
"labels": labels,
}
return service.common.update(state, params, update)

View file

@ -1,17 +1,15 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import (
State,
)
from ansible_collections.snailed.ez_compose.plugins.module_utils.models import State
EXTRA_ARGS = {}
def helper(state: State) -> State:
return state
def helper(_state: State, _params: dict[str, Any]) -> dict[str, Any]:
return {}

View file

@ -1,4 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
from __future__ import annotations
@ -92,6 +92,7 @@ def settings_spec() -> dict[str, Any]:
for arg in service_args.values():
arg.pop("required", None)
arg.pop("default", None)
settings["options"]["service_default_args"]["options"][module_name] = {
"type": "dict",
@ -103,6 +104,7 @@ def settings_spec() -> dict[str, Any]:
for arg in label_args.values():
arg.pop("required", None)
arg.pop("default", None)
settings["options"]["label_default_args"]["options"][module_name] = {
"type": "dict",

View file

@ -1,10 +1,11 @@
#!/usr/bin/python
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# Copyright: (c) 2025, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
# ruff: noqa: E402
from __future__ import annotations
from pprint import pp
from dataclasses import asdict
# TODO: break this down per module
# TODO: generate this by reassembling
@ -477,18 +478,40 @@ def main() -> None:
"settings": spec.settings_spec(),
"services": spec.service_argument_spec(),
},
supports_check_mode=True,
)
if not find_spec("yaml"):
module.fail_json("PyYAML seems to be missing on host") # pyright: ignore[reportUnknownMemberType]
module.fail_json("PyYAML needs to be installed on the host to use this plugin") # pyright: ignore[reportUnknownMemberType]
state = common.get_state(module)
try:
state = common.get_state(module)
except Exception as e: # noqa: BLE001
module.fail_json(f"Error while reading existing compose file: {e}") # pyright: ignore[reportUnknownMemberType]
for name, services_params in [(x, y) for x, y in module.params["services"].items() if y]:
for service_params in services_params:
for index, service_params in enumerate(services_params):
params = service.common.get_default_args(state, name) | common.clean_none(
service_params
)
params["_index"] = index
helper = getattr(service, name).helper
state = service.common.run_helper(state, service_params, helper)
pp(state.after)
state = service.common.run_helper(state, params, helper)
state = common.update_project(state)
state = common.set_result(state)
if state.result.changed and not module.check_mode:
try:
state = common.write_compose(state)
except Exception as e: # noqa: BLE001
module.fail_json(f"Error while writing new compose file: {e}") # pyright: ignore[reportUnknownMemberType]
ret = asdict(state.result)
if not module._diff: # noqa: SLF001
del ret["diff"]
module.exit_json(**ret) # pyright: ignore[reportUnknownMemberType]
if __name__ == "__main__":

View file

@ -11,25 +11,29 @@ ignore = [
"TD002",
"TD003",
"INP001",
"COM812",
"D100",
"D101",
"D103",
"D104",
"D203",
"D213",
]
[tool.ruff.lint.per-file-ignores]
"tests/units/**" = [
"S101",
"PT009",
]
[tool.ruff.format]
line-ending = "lf"
[tool.ruff.lint.mccabe]
max-complexity = 10
[tool.ruff.lint.pydocstyle]
convention = "numpy"
[tool.basedpyright]
typeCheckingMode = "strict"
# Handled by ruff
reportPrivateUsage = false
reportIgnoreCommentWithoutRule = false
reportUnusedImport = false
reportUnusedClass = false