Compare commits
No commits in common. "d1e4452a5da53d069b144b60e70d73a6bfece716" and "015f2e5a168b4ca291f0ca3dcb090de01f6d7f74" have entirely different histories.
d1e4452a5d
...
015f2e5a16
4 changed files with 41 additions and 196 deletions
134
containerctl
134
containerctl
|
@ -1,134 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
BASEDIR="/var/lib/containerctl"
|
|
||||||
CONTAINERDIR="${BASEDIR}/containers"
|
|
||||||
CONFIGDIR="${BASEDIR}/configs"
|
|
||||||
LOGDIR="/var/log/containerctl"
|
|
||||||
TODAY="$(date '+%F')"
|
|
||||||
LOG="${LOGDIR}/${TODAY}"
|
|
||||||
|
|
||||||
log_error()
|
|
||||||
{
|
|
||||||
printf '[%b] (EE) %b\n' "${TODAY}" "${@}" | tee -a "${LOG}"
|
|
||||||
}
|
|
||||||
|
|
||||||
list_dir()
|
|
||||||
{
|
|
||||||
find "${1}" -mindepth 1 -type d
|
|
||||||
}
|
|
||||||
|
|
||||||
list_containers()
|
|
||||||
{
|
|
||||||
list_dir "${CONTAINERDIR}"
|
|
||||||
}
|
|
||||||
|
|
||||||
list_configs()
|
|
||||||
{
|
|
||||||
list_dir "${CONFIGDIR}"
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_script()
|
|
||||||
{
|
|
||||||
container="${1}"
|
|
||||||
script="${2}"
|
|
||||||
|
|
||||||
if [ -z "${container}" ]
|
|
||||||
then
|
|
||||||
log_error "No container passed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "${script}" ]
|
|
||||||
then
|
|
||||||
log_error "No script passed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -e "${CONTAINERDIR}/${container}" ]
|
|
||||||
then
|
|
||||||
log_error "${container} does not exist, did you forget to generate it?"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -d "${CONTAINERDIR}/${container}" ]
|
|
||||||
then
|
|
||||||
log_error "${container} does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -e "${CONTAINERDIR}/${container}/${script}" ]
|
|
||||||
then
|
|
||||||
log_error "${script} is invalid, maybe try regenerating the container?"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -f "${CONTAINERDIR}/${container}/${script}" ]
|
|
||||||
then
|
|
||||||
log_error "${script} is invalid, please recreate the container"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
/bin/sh "${CONTAINERDIR}/${container}/${script}" || log_error "${script} failed!"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
generate_container()
|
|
||||||
{
|
|
||||||
config="${1}"
|
|
||||||
|
|
||||||
if [ -z "${config}" ]
|
|
||||||
then
|
|
||||||
log_error "No config was passed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -e "${CONFIGDIR}" ]
|
|
||||||
then
|
|
||||||
log_error "${CONFIGDIR} does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -d "${CONFIGDIR}" ]
|
|
||||||
then
|
|
||||||
log_error "${CONFIGDIR} is not a directory"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -e "${CONFIGDIR}/${config}" ]
|
|
||||||
then
|
|
||||||
log_error "${config} does not exist!" \
|
|
||||||
"Please check the name and or location of the desired config."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -f "${CONFIGDIR}/${config}" ]
|
|
||||||
then
|
|
||||||
log_error "${config} is not a file."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
/usr/bin/env python3 "${BASEDIR}/generate/generate.py" \
|
|
||||||
"${CONFIGDIR}/${config}" "${LOG}" "${CONTAINERDIR}"
|
|
||||||
|
|
||||||
if [ "${?}" = 1 ]
|
|
||||||
then
|
|
||||||
log_error "Container generation failed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_all()
|
|
||||||
{
|
|
||||||
list_configs | while read -r config
|
|
||||||
do
|
|
||||||
generate_config "$(printf '%b' "${config}" | sed -e "s|${CONFIGDIR}||g")"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
case "${1}" in
|
|
||||||
"list-containers") list_containers ;;
|
|
||||||
"list-configs") list_configs ;;
|
|
||||||
"generate-all") generate_all ;;
|
|
||||||
"generate") shift; generate_config "${@}" ;;
|
|
||||||
*) exec_script "${@}" ;;
|
|
||||||
esac
|
|
|
@ -17,13 +17,25 @@ class ConfigError(Exception):
|
||||||
def __init__(self, message: str = "") -> None:
|
def __init__(self, message: str = "") -> None:
|
||||||
"""Create Exception object."""
|
"""Create Exception object."""
|
||||||
self.message = message
|
self.message = message
|
||||||
super().__init__(message)
|
self.super.__init__(message)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
"""Convert Exception object to a string."""
|
"""Convert Exception object to a string."""
|
||||||
return f"Configuration error: {self.message}"
|
return f"Configuration error: {self.message}"
|
||||||
|
|
||||||
|
|
||||||
|
def join(data: list, separator: str = ",") -> str:
|
||||||
|
"""Join a list together."""
|
||||||
|
ret: str = ""
|
||||||
|
x = data.copy()
|
||||||
|
if len(x) == 0:
|
||||||
|
return ret
|
||||||
|
while len(x) > 1:
|
||||||
|
ret += x.pop() + separator
|
||||||
|
ret += x.pop()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def maybe(json: dict, key: str) -> str | dict | list | bool | None:
|
def maybe(json: dict, key: str) -> str | dict | list | bool | None:
|
||||||
"""Maybe get a value."""
|
"""Maybe get a value."""
|
||||||
try:
|
try:
|
||||||
|
@ -32,11 +44,20 @@ def maybe(json: dict, key: str) -> str | dict | list | bool | None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def command_join(items: list | None) -> str:
|
||||||
|
"""Like join(), but call command()."""
|
||||||
|
joined = ""
|
||||||
|
if items is not None:
|
||||||
|
for item in items:
|
||||||
|
joined += " " + item.command()
|
||||||
|
return joined
|
||||||
|
|
||||||
|
|
||||||
def trim(s: str) -> str:
|
def trim(s: str) -> str:
|
||||||
"""Remove sequential whitespace."""
|
"""Remove sequential whitespace."""
|
||||||
s = s.replace("\t ", "\t")
|
s = s.replace("\t ", "\t")
|
||||||
s = s.replace("\t\\\n", " ")
|
s = s.replace("\t\\\n", " ")
|
||||||
while " " in s:
|
while s.__contains__(" "):
|
||||||
s = s.replace(" ", " ")
|
s = s.replace(" ", " ")
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
@ -120,8 +141,7 @@ class Secret:
|
||||||
cmd = (
|
cmd = (
|
||||||
f"--secret {self.name},type={self.secret_type},target={self.target}"
|
f"--secret {self.name},type={self.secret_type},target={self.target}"
|
||||||
)
|
)
|
||||||
# Not a password, ruff...
|
if self.target == "mount" and self.options != "":
|
||||||
if self.secret_type == "mount" and self.options != "": # noqa: S105
|
|
||||||
cmd = f"{cmd},{self.options}"
|
cmd = f"{cmd},{self.options}"
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -216,15 +236,10 @@ class Ports:
|
||||||
def command(self) -> str:
|
def command(self) -> str:
|
||||||
"""Option for podman container create."""
|
"""Option for podman container create."""
|
||||||
ports = ""
|
ports = ""
|
||||||
seperator = " \\\n"
|
for port in self.tcp_ports:
|
||||||
ports += seperator.join(
|
ports += f"\t--port {port}/tcp \\\n"
|
||||||
[f"\t--port {port}/tcp" for port in self.tcp_ports]
|
for port in self.udp_ports:
|
||||||
)
|
ports += f"\t--port {port}/udp \\\n"
|
||||||
ports += seperator
|
|
||||||
ports += seperator.join(
|
|
||||||
[f"\t--port {port}/udp" for port in self.udp_ports]
|
|
||||||
)
|
|
||||||
ports += seperator
|
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
|
|
||||||
|
@ -254,7 +269,7 @@ class Network:
|
||||||
if self.mode == "":
|
if self.mode == "":
|
||||||
return ""
|
return ""
|
||||||
cmd = f"--network={self.mode}"
|
cmd = f"--network={self.mode}"
|
||||||
opts = ",".join(self.options)
|
opts = join(self.options)
|
||||||
if opts != "":
|
if opts != "":
|
||||||
cmd += f":{opts}"
|
cmd += f":{opts}"
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -330,15 +345,15 @@ class Dns:
|
||||||
|
|
||||||
def command(self) -> str:
|
def command(self) -> str:
|
||||||
"""Option for podman container create."""
|
"""Option for podman container create."""
|
||||||
if len(self.servers) == 0 and self.search == "":
|
if len(self.servers) == 0 and self.serach == "":
|
||||||
return ""
|
return ""
|
||||||
if len(self.servers) == 0:
|
if len(self.servers) == 0:
|
||||||
return f"--dns-search={self.search}"
|
return f"--dns-search={self.search}"
|
||||||
if self.search == "":
|
if self.search == "":
|
||||||
return f"--dns={','.join(self.servers)}"
|
return f"--dns={join(self.servers)}"
|
||||||
|
|
||||||
cmd = f"--dns-search={self.search} \\\n\t--dns="
|
cmd = f"--dns-search={self.search} \\\n\t--dns="
|
||||||
cmd += ",".join(self.servers)
|
cmd += join(self.servers)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
@ -432,7 +447,7 @@ class Container:
|
||||||
|
|
||||||
def create_container(self) -> str:
|
def create_container(self) -> str:
|
||||||
"""Generate podman container create command."""
|
"""Generate podman container create command."""
|
||||||
cmd = f"# Create container {self.name}\n"
|
cmd = f"# Create container {self.name}"
|
||||||
cmd += "podman container crate \\\n"
|
cmd += "podman container crate \\\n"
|
||||||
cmd += f"\t--name={self.name} \\\n"
|
cmd += f"\t--name={self.name} \\\n"
|
||||||
if self.privileged:
|
if self.privileged:
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
"""Generate the control files.."""
|
"""Generate the control files.."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import stat
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
@ -49,21 +48,17 @@ def create_container_from_config(data: dict, log: Log) -> Container | None:
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""Run the program."""
|
"""Run the program."""
|
||||||
config_threshold = 2
|
argc_threshold = 2
|
||||||
log_threshold = 3
|
if len(sys.argv) < argc_threshold:
|
||||||
if len(sys.argv) < config_threshold:
|
|
||||||
logger = Log("/dev/stdout")
|
logger = Log("/dev/stdout")
|
||||||
logger.log_error("No arguments passed!")
|
logger.log_error("No arguments passed!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
config_file = ""
|
config_file = ""
|
||||||
log_file = ""
|
log_file = ""
|
||||||
base = "/var/lib/containerctl/containers"
|
if len(sys.argv) >= argc_threshold:
|
||||||
if len(sys.argv) >= config_threshold:
|
|
||||||
config_file = sys.argv[1]
|
config_file = sys.argv[1]
|
||||||
if len(sys.argv) >= log_threshold:
|
if len(sys.argv) > argc_threshold:
|
||||||
log_file = sys.argv[2]
|
log_file = sys.argv[2]
|
||||||
if len(sys.argv) > log_threshold:
|
|
||||||
base = sys.argv[3]
|
|
||||||
logger = Log(log_file)
|
logger = Log(log_file)
|
||||||
data = load_container_config(Path(config_file), logger)
|
data = load_container_config(Path(config_file), logger)
|
||||||
if data is None:
|
if data is None:
|
||||||
|
@ -71,42 +66,11 @@ def main() -> None:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
ct = create_container_from_config(data, logger)
|
ct = create_container_from_config(data, logger)
|
||||||
except ConfigError as e:
|
except Exception as e: # noqa: BLE001
|
||||||
logger.log_error(e)
|
logger.log_error(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
scripts = {
|
print(ct)
|
||||||
"create-volumes": ct.create_volumes,
|
print(vars(ct))
|
||||||
"create-secrets": ct.create_secrets,
|
|
||||||
"create-environment": ct.create_environment,
|
|
||||||
"remove-volumes": ct.remove_volumes,
|
|
||||||
"remove-secrets": ct.remove_secrets,
|
|
||||||
"create": ct.create_container,
|
|
||||||
"remove": ct.remove_container,
|
|
||||||
"purge": ct.purge_container,
|
|
||||||
"start": ct.start_container,
|
|
||||||
"stop": ct.stop_container,
|
|
||||||
"restart": ct.restart_container,
|
|
||||||
"upgrade": ct.upgrade_container,
|
|
||||||
}
|
|
||||||
base = f"{base}/{ct.name}"
|
|
||||||
if not Path(base).exists():
|
|
||||||
Path(base).mkdir()
|
|
||||||
for script, method in scripts.items():
|
|
||||||
s = Path(f"{base}/{script}")
|
|
||||||
with s.open("w+", encoding="utf-8") as f:
|
|
||||||
f.write("#!/bin/sh\n")
|
|
||||||
if script == "create":
|
|
||||||
f.write(ct.create_volumes())
|
|
||||||
f.write(ct.create_secrets())
|
|
||||||
f.write(ct.create_environment())
|
|
||||||
f.write(method())
|
|
||||||
s.chmod(
|
|
||||||
stat.S_IRWXU
|
|
||||||
| stat.S_IRGRP
|
|
||||||
| stat.S_IXGRP
|
|
||||||
| stat.S_IROTH
|
|
||||||
| stat.S_IXOTH,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -64,7 +64,7 @@ class Log:
|
||||||
print(msg)
|
print(msg)
|
||||||
if self.logfile == Path("/dev/stdout"):
|
if self.logfile == Path("/dev/stdout"):
|
||||||
return
|
return
|
||||||
with self.logfile.open("a+", encoding="utf-8") as f:
|
with self.logfile.open("a+") as f:
|
||||||
f.write(msg)
|
f.write(msg)
|
||||||
|
|
||||||
def timestamp(self) -> str:
|
def timestamp(self) -> str:
|
||||||
|
|
Loading…
Add table
Reference in a new issue