Compare commits
9 commits
aa13b77758
...
d5ea5e64ee
Author | SHA1 | Date | |
---|---|---|---|
d5ea5e64ee | |||
9805419555 | |||
b3fe3b8a10 | |||
94fcd6828f | |||
d67137363f | |||
2e793fd31f | |||
11d6b574f3 | |||
e3125ea4fe | |||
8f156e9f70 |
4 changed files with 92 additions and 25 deletions
49
containerctl
49
containerctl
|
@ -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
|
||||||
|
|
|
@ -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()}"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue