diff --git a/image@.service b/image@.service index 09af882..1f653ba 100644 --- a/image@.service +++ b/image@.service @@ -13,7 +13,7 @@ ExecCondition=/usr/bin/bash -c '[[ $(podman images --format json localhost/%i | Environment="IMAGE_TAG=localhost/%i" "BUILD_SOURCE=/docker/containers/%i" ExecStart=/usr/bin/podman build --tag ${IMAGE_TAG} $EXTRAOPTS ${BUILD_SOURCE} Restart=no -RemainAfterExit=yes +RemainAfterExit=no TimeoutStartSec=1h SendSIGKILL=no LogRateLimitIntervalSec=15min diff --git a/imagerebuild.py b/imagerebuild.py new file mode 100755 index 0000000..beb5001 --- /dev/null +++ b/imagerebuild.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +import logging +import pathlib +from typing import Set + +import click +import sh +from sh import systemctl +from sh import podman + +SERVICES_BASE_PATH = "/docker/services/" + + +def resolve_image_units(): + services_path = pathlib.Path(SERVICES_BASE_PATH) + services_list = list(map(lambda p: str(p.name), services_path.iterdir())) + image_units: Set[str] = set() + + def process_pod_systemctl_show(line: str): + search_str = "Wants=" + if line.startswith(search_str): + for unit in line[len(search_str) :].split(" "): + if unit.startswith("image@") and unit.endswith(".service"): + image_units.add(unit) + + with click.progressbar( + length=len(services_list) * 2, label="Collecting container image services.." + ) as bar: + started_processes = [] + for service in services_list: + process = systemctl.show( + f"pod@{service}.service", + _out=process_pod_systemctl_show, + _bg=True, + _done=lambda cmd, success, exit_code: bar.update(1), + ) + started_processes.append(process) + # join processes + [p.wait() for p in started_processes] + + first = True + new_image_units: Set[str] = set(image_units) + previous_image_units: Set[str] = set(image_units) + bar.length = len(image_units) * 2 + + while first or len(new_image_units) > 0: + first = False + started_processes = [] + for service in new_image_units: + process = systemctl.show( + service, + _out=process_pod_systemctl_show, + _bg=True, + _done=lambda cmd, success, exit_code: bar.update(1), + ) + started_processes.append(process) + # join processes + [p.wait() for p in started_processes] + new_image_units = image_units.difference(previous_image_units) + bar.length += len(new_image_units) + previous_image_units = set(image_units) + + return image_units + + +def main(): + logging.basicConfig(level=logging.CRITICAL) + image_units = resolve_image_units() + image_tags: Set[str] = set() + + def process_image_systemctl_show(line: str): + search_str = "Environment=" + if line.startswith(search_str): + for unit in line[len(search_str) :].split(" "): + search_str = "IMAGE_TAG=" + if unit.startswith(search_str): + image_tags.add(unit[len(search_str) :]) + + started_processes = [] + with click.progressbar(image_units, label="Collecting container images..") as bar: + for image_service in bar: + process = systemctl.show( + image_service, + _out=process_image_systemctl_show, + _bg=True, + _done=lambda cmd, success, exit_code: bar.update(1), + ) + started_processes.append(process) + # join processes + [p.wait() for p in started_processes] + + started_processes = [] + with click.progressbar( + image_tags, label="Untagging container images..", show_pos=True + ) as bar: + for image_tag in bar: + process = podman.untag( + image_tag, + _bg=True, + _done=lambda cmd, success, exit_code: bar.update(1), + ) + started_processes.append(process) + # join processes + for p in started_processes: + try: + p.wait() + except sh.ErrorReturnCode: + # ignore missing tags + if "image not known".encode() not in p.stderr: + raise + + started_processes = [] + with click.progressbar( + image_units, label="Building images..", show_pos=True + ) as bar: + for image_service in bar: + process = systemctl.restart( + image_service, + _bg=True, + _done=lambda cmd, success, exit_code: bar.update(1), + ) + started_processes.append(process) + # join processes + [p.wait() for p in started_processes] + + +if __name__ == "__main__": + main()