1
0
Fork 0

Compare commits

...

9 commits

Author SHA1 Message Date
d5ea5e64ee
generate: generate: Bump version to 0.0.13
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-11 17:52:42 +02:00
9805419555
containerctl: Silence tee error messages
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-11 17:52:13 +02:00
b3fe3b8a10
containerctl: Add usage message
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-11 17:51:50 +02:00
94fcd6828f
containerctl: Expand python version check
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-11 17:47:52 +02:00
d67137363f
containerctl: Fix python version check
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-11 17:39:09 +02:00
2e793fd31f
generate: generate: Set the logging prefix after loading the json file
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-11 17:38:30 +02:00
11d6b574f3
generate: log: Add a log prefix
Add a log prefix. This enables logging the config file that contained
the error or warning, which is helpful when regenerating all containers.

Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-11 17:37:32 +02:00
e3125ea4fe
generate: generate: Bump to 0.0.12
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-09 20:19:35 +02:00
8f156e9f70
generate: container: Combine Network, Dns and Ports to ContainerNetwork
Signed-off-by: Enno Tensing <tenno@suij.in>
2025-08-09 20:19:30 +02:00
4 changed files with 92 additions and 25 deletions

View file

@ -11,9 +11,42 @@ LOGDIR="/var/log/containerctl"
TODAY="$(date '+%F')" TODAY="$(date '+%F')"
LOG="${LOGDIR}/${TODAY}" LOG="${LOGDIR}/${TODAY}"
get_python_path()
{
py="python3"
pyver="$(/usr/bin/env "${py}" -c 'import sys; print(sys.version_info.minor)')"
if [ "${pyver}" -lt "11" ]
then
py="python3.13"
else
printf '%b' "${py}"
return
fi
if /usr/bin/env "${py}" 2> /dev/null
then
return
fi
py="python3.12"
if /usr/bin/env "${py}" 2> /dev/null
then
return
fi
py="python3.11"
if /usr/bin/env "${py}" 2> /dev/null
then
return
fi
log_error 'containerctl needs at least Python 3.11 to run!'
exit 1
}
log_error() log_error()
{ {
printf '[%b] (EE) %b\n' "${TODAY}" "${@}" | tee -a "${LOG}" printf '[%b] (EE) %b\n' "${TODAY}" "${@}" | tee -a "${LOG}" 2> /dev/null
} }
list_containers() list_containers()
@ -106,12 +139,7 @@ generate_container()
exit 1 exit 1
fi fi
mypython="python3" mypython="$(get_python_path)"
pyver="$(/usr/bin/env "${mypython}" -c 'import sys; print(sys.version_info.minor)')"
if [ "${pyver}" -gt "11" ]
then
mypython="python3.11"
fi
/usr/bin/env "${mypython}" "${BASEDIR}/generate/generate.py" \ /usr/bin/env "${mypython}" "${BASEDIR}/generate/generate.py" \
"${CONFIGDIR}/${config}" "${LOG}" "${CONTAINERDIR}" "${CONFIGDIR}/${config}" "${LOG}" "${CONTAINERDIR}"
@ -130,10 +158,17 @@ generate_all()
done done
} }
usage()
{
printf '%b list-containers|list-configs|generate CONFIG-FILE|generate-all|CONTAINER-NAME ACTION\n' "${0}"
}
case "${1}" in case "${1}" in
"list-containers") list_containers ;; "list-containers") list_containers ;;
"list-configs") list_configs ;; "list-configs") list_configs ;;
"generate-all") generate_all ;; "generate-all") generate_all ;;
"generate") shift; generate_container "${@}" ;; "generate") shift; generate_container "${@}" ;;
"help") usage "${0}" ;;
"usage") usage "${0}" ;;
*) exec_script "${@}" ;; *) exec_script "${@}" ;;
esac esac

View file

@ -415,7 +415,7 @@ class Environment:
if cmd == "": if cmd == "":
return "" return ""
return header + cmd return header + f"printf '\\n' > {self.file}\n" + cmd
def remove(self) -> str: def remove(self) -> str:
"""Remove env file.""" """Remove env file."""
@ -665,15 +665,44 @@ class ContainerOptions:
return "" return ""
@dataclass
class ContainerNetwork:
"""Wrapper for Network, Dns and Ports."""
network: Network
dns: Dns
ports: Ports
@classmethod
def from_json(cls, json: ConfigValue, logger: Log) -> Self:
"""Create from JSON."""
network_config = maybe(json, "network")
dns_config = maybe(json, "dns")
ports_config = maybe(json, "ports")
network = Network.from_json(network_config, logger)
dns = Dns.from_json(dns_config, logger)
ports = Ports.from_json(ports_config, logger)
return cls(network, dns, ports)
def command(self) -> str:
"""Option for podman container create."""
cmd = ""
cmd += self.network.command()
cmd += self.dns.command()
cmd += self.ports.command()
return cmd
class Container: class Container:
"""Container.""" """Container."""
name: str name: str
image: Image image: Image
ct_opts: ContainerOptions ct_opts: ContainerOptions
network: Network ct_network: ContainerNetwork
dns: Dns
ports: Ports
env: Environment env: Environment
secrets: list secrets: list
volumes: list volumes: list
@ -695,9 +724,6 @@ class Container:
ct_opts = ContainerOptions.from_json(json, logger) ct_opts = ContainerOptions.from_json(json, logger)
if not ct_opts.is_valid: if not ct_opts.is_valid:
return return
network = maybe(json, "network")
dns = maybe(json, "dns")
ports = maybe(json, "ports")
env = maybe(json, "env") env = maybe(json, "env")
secrets = maybe(json, "secrets") secrets = maybe(json, "secrets")
volumes = maybe(json, "volumes") volumes = maybe(json, "volumes")
@ -706,9 +732,7 @@ class Container:
self.name = str(name) self.name = str(name)
self.image = Image.from_json(image, logger) self.image = Image.from_json(image, logger)
self.ct_opts = ct_opts self.ct_opts = ct_opts
self.network = Network.from_json(network, logger) self.ct_network = ContainerNetwork.from_json(json, logger)
self.dns = Dns.from_json(dns, logger)
self.ports = Ports.from_json(ports, logger)
self.env = Environment.from_json(env, logger) self.env = Environment.from_json(env, logger)
self.env.file = "/var/lib/containerctl/environment-files/" self.env.file = "/var/lib/containerctl/environment-files/"
self.env.file += f"{self.name}" self.env.file += f"{self.name}"
@ -747,9 +771,7 @@ class Container:
cmd += "podman container create \\\n" cmd += "podman container create \\\n"
cmd += f"\t--name={self.name} \\\n" cmd += f"\t--name={self.name} \\\n"
cmd += f"{self.ct_opts.command()}" cmd += f"{self.ct_opts.command()}"
cmd += f"{self.network.command()}" cmd += f"{self.ct_network.command()}"
cmd += f"{self.dns.command()}"
cmd += f"{self.ports.command()}"
cmd += f"{self.env.command()}" cmd += f"{self.env.command()}"
for secret in self.secrets: for secret in self.secrets:
cmd += f"{secret.command()}" cmd += f"{secret.command()}"

View file

@ -14,7 +14,7 @@ from pathlib import Path
from container import ConfigError, Container from container import ConfigError, Container
from log import Log from log import Log
GENERATE_VERSION = "0.0.11" GENERATE_VERSION = "0.0.13"
HEADER = f"""#!/bin/sh HEADER = f"""#!/bin/sh
# This script was generated by containerctl v{GENERATE_VERSION} # This script was generated by containerctl v{GENERATE_VERSION}
# Report bugs with _this script_ to <tenno+containerctl@suij.in> # Report bugs with _this script_ to <tenno+containerctl@suij.in>
@ -80,6 +80,7 @@ def main() -> None:
if data is None: if data is None:
logger.log_error(f"{config_file} is invalid, aborting!") logger.log_error(f"{config_file} is invalid, aborting!")
sys.exit(1) sys.exit(1)
logger.set_prefix(Path(config_file).name)
ct = create_container_from_config(data, logger) ct = create_container_from_config(data, logger)
if ct is None: if ct is None:
sys.exit(1) sys.exit(1)

View file

@ -15,6 +15,7 @@ class Log:
messages: list = [] messages: list = []
logfile: Path logfile: Path
prefix: str = ""
def __init__(self, path: str) -> None: def __init__(self, path: str) -> None:
"""Init for Log.""" """Init for Log."""
@ -25,15 +26,19 @@ class Log:
def log_error(self, msg: str) -> None: def log_error(self, msg: str) -> None:
"""Log an error.""" """Log an error."""
now = self.timestamp() now = self.timestamp()
prefix = "EE" prefix = "(EE)"
log_message = f"[{now}] ({prefix}) {msg}" if self.prefix != "":
prefix += f" {self.prefix}:"
log_message = f"[{now}] {prefix} {msg}"
self.write_message(log_message) self.write_message(log_message)
def log_warning(self, msg: str) -> None: def log_warning(self, msg: str) -> None:
"""Log a warning.""" """Log a warning."""
now = self.timestamp() now = self.timestamp()
prefix = "WW" prefix = "(WW)"
log_message = f"[{now}] ({prefix}) {msg}" if self.prefix != "":
prefix += f" {self.prefix}:"
log_message = f"[{now}] {prefix} {msg}"
self.write_message(log_message) self.write_message(log_message)
def write_message(self, msg: str) -> None: def write_message(self, msg: str) -> None:
@ -49,3 +54,7 @@ class Log:
return datetime.datetime.now(tz=datetime.UTC).strftime( return datetime.datetime.now(tz=datetime.UTC).strftime(
"%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M:%S",
) )
def set_prefix(self, prefix: str) -> None:
"""Set a prefix."""
self.prefix = prefix