podlaunch/main.py

141 lines
5.1 KiB
Python
Raw Normal View History

2020-11-14 17:50:04 +00:00
import json
import os
import pathlib
import sys
import threading
import traceback
from datetime import datetime
2020-11-14 23:09:45 +00:00
from signal import signal, SIGHUP, SIGINT, SIGTERM, setitimer, SIGALRM, ITIMER_REAL
2020-11-14 17:50:04 +00:00
import click
import sh
# noinspection PyUnresolvedReferences
from sh import podman
SERVICES_BASE_PATH = "/docker/services/"
sdnotify = sh.Command("systemd-notify")
class PodKeeper:
def __init__(self, network, stop_previous, identifier):
2020-11-14 17:50:04 +00:00
self.podnet_args = ("--network", network) if network else ()
self.stop_previous = stop_previous
2020-11-14 17:50:04 +00:00
identifier_path = pathlib.PurePath(identifier)
if len(identifier_path.parts) != 1:
2020-11-14 23:09:45 +00:00
raise ValueError(f"identifier has path parts: {identifier_path}")
2020-11-14 17:50:04 +00:00
self.podhome = pathlib.Path(SERVICES_BASE_PATH) / identifier_path
if not self.podhome.exists():
raise NotADirectoryError(f"pod home does not exist: {self.podhome}")
self.podname = f"{identifier}_pod"
self.podyaml = f"pod-{identifier}.yaml"
podyaml_complete = (self.podhome / self.podyaml)
if not podyaml_complete.exists():
raise FileNotFoundError(f"pod definition does not exist: {podyaml_complete}")
self.stopping = threading.Event()
self.reloading = threading.Event()
self.checking = threading.Event()
self.waiter = threading.Event()
2020-11-14 23:09:45 +00:00
self.last_check = datetime.utcnow()
2020-11-14 17:50:04 +00:00
2020-11-14 17:56:50 +00:00
def destroy(self, signum, stackframe):
2020-11-14 22:29:08 +00:00
print("Destroy signal", signum, file=sys.stderr, flush=True)
2020-11-14 17:50:04 +00:00
self.stopping.set()
self.waiter.set()
2020-11-14 17:56:50 +00:00
def reload(self, signum, stackframe):
2020-11-14 22:29:08 +00:00
print("Reload signal", signum, file=sys.stderr, flush=True)
2020-11-14 17:50:04 +00:00
self.reloading.set()
self.waiter.set()
2020-11-14 17:56:50 +00:00
def check(self, signum, stackframe):
2020-11-14 17:50:04 +00:00
self.checking.set()
self.waiter.set()
def run(self):
os.chdir(self.podhome)
if self.stop_previous and podman.pod.exists(self.podname).exit_code == 0:
print(f"Stopping pod {self.podname}", file=sys.stderr, flush=True)
podman.pod.stop(self.podname)
2020-11-14 23:09:45 +00:00
print(f"Starting pod {self.podname} at {self.last_check}", file=sys.stderr, flush=True)
2020-11-14 17:50:04 +00:00
podman.play.kube(self.podyaml, *self.podnet_args)
2020-11-14 17:59:47 +00:00
try:
if 'NOTIFY_SOCKET' in os.environ:
2020-11-18 23:38:10 +00:00
sdnotify("--ready", f"--pid={os.getpid()}", "--status=Monitoring pod...")
2020-11-14 17:59:47 +00:00
while not self.stopping.is_set():
self.waiter.wait()
self.waiter.clear()
2020-11-14 23:09:45 +00:00
2020-11-14 17:59:47 +00:00
if self.checking.is_set():
self.checking.clear()
2020-11-14 23:09:45 +00:00
self.check_pod()
2020-11-14 17:50:04 +00:00
2020-11-14 17:59:47 +00:00
if self.reloading.is_set():
self.reloading.clear()
2020-11-14 23:09:45 +00:00
self.reload_pod()
2020-11-14 17:50:04 +00:00
2020-11-18 22:41:14 +00:00
if 'NOTIFY_SOCKET' in os.environ:
2020-11-18 23:38:10 +00:00
sdnotify("--status=Stopping pod")
2020-11-14 17:59:47 +00:00
finally:
2020-11-14 23:09:45 +00:00
self.stop_pod()
def reload_pod(self):
print("Reloading pod", self.podname, file=sys.stderr, flush=True)
try:
podman.pod.kill("--signal", "HUP", self.podname)
except sh.ErrorReturnCode:
print("Error reloading pod", file=sys.stderr, flush=True)
traceback.print_exc()
def check_pod(self):
new_timestamp = datetime.utcnow()
inspect_command = podman.pod.inspect(self.podname)
pod_description = json.loads(inspect_command.stdout)
for container in pod_description["Containers"]:
if container["State"] != "running":
print(f"Container {container['Name']} exited", file=sys.stderr, flush=True)
logs = podman.logs('--since', self.last_check.isoformat(), container['Name'])
print(f"Log since last check:\n{logs}", file=sys.stderr, flush=True)
self.stopping.set()
self.last_check = new_timestamp
2020-11-14 17:50:04 +00:00
2020-11-14 23:09:45 +00:00
def stop_pod(self):
2020-11-14 17:50:04 +00:00
print("Stopping pod", self.podname, file=sys.stderr, flush=True)
try:
podman.pod.stop("-t", "19", self.podname)
successful_stopped = True
except sh.ErrorReturnCode:
print(f"First stop of {self.podname} was not successful!", file=sys.stderr, flush=True)
successful_stopped = False
try:
podman.pod.stop("-t", "5", self.podname)
except sh.ErrorReturnCode:
if not successful_stopped:
print(f"Second stop of {self.podname} was not successful!", file=sys.stderr, flush=True)
try:
podman.pod.rm(self.podname)
except sh.ErrorReturnCode:
print(f"Removal of {self.podname} was not successful!", file=sys.stderr, flush=True)
@click.command()
@click.option("--network", default="brodge", help="Network for the created pod")
@click.option("--stop-previous", default=True, help="Stop previously running pod with the same name")
2020-11-14 17:50:04 +00:00
@click.argument("identifier")
def main(network, stop_previous, identifier):
keeper = PodKeeper(network, stop_previous, identifier)
2020-11-14 17:50:04 +00:00
signal(SIGINT, keeper.destroy)
signal(SIGTERM, keeper.destroy)
signal(SIGHUP, keeper.reload)
signal(SIGALRM, keeper.check)
setitimer(ITIMER_REAL, 4.0, 10.0)
keeper.run()
if __name__ == '__main__':
main()