86 lines
3.1 KiB
Python
Executable File
86 lines
3.1 KiB
Python
Executable File
import argparse
|
|
import datetime
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
import docker
|
|
from pytz import timezone
|
|
|
|
# Set up logging
|
|
LOG_FILE = Path("/mnt/docker-persistent-data/docker/update-containers.log")
|
|
LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Clear the log file by opening it in write mode before configuring logging
|
|
with open(LOG_FILE, 'w'):
|
|
pass
|
|
|
|
logging.basicConfig(filename=LOG_FILE, level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%H:%M:%S')
|
|
|
|
DEFAULT_STACKS = ["traefik", "nextcloud", "passbolt", "searxng", "gitea"]
|
|
DOCKER_PATH = Path.home() / "docker"
|
|
|
|
def run_compose_command(service_dir: Path, command: list[str]):
|
|
"""Run a docker compose CLI command in the given directory."""
|
|
try:
|
|
result = subprocess.run(
|
|
["docker", "compose"] + command,
|
|
cwd=service_dir,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
check=True
|
|
)
|
|
logging.info(result.stdout)
|
|
except subprocess.CalledProcessError as e:
|
|
logging.error(f"Error running {' '.join(e.cmd)}:\n{e.output}")
|
|
raise
|
|
|
|
def check_container_status(docker_client, service_name):
|
|
containers = docker_client.containers.list(all=True, filters={'name': service_name})
|
|
for container in containers:
|
|
container_info = docker_client.api.inspect_container(container.id)
|
|
container_state = container_info['State']
|
|
if not container_state['Running']:
|
|
logging.error(f"Container {container.name} for service {service_name} is not running. Status: {container_state['Status']}")
|
|
|
|
def main(stacks):
|
|
current_time = datetime.datetime.now(timezone("Australia/Brisbane"))
|
|
logging.info("=== Update started: %s ===", current_time.strftime("%Y-%m-%d %H:%M:%S"))
|
|
|
|
os.chdir(DOCKER_PATH)
|
|
docker_client = docker.from_env()
|
|
|
|
for service in stacks:
|
|
service_dir = DOCKER_PATH / service
|
|
if not service_dir.exists():
|
|
logging.warning(f"Skipping {service} (directory does not exist)")
|
|
continue
|
|
|
|
logging.info(f"Updating service: {service}")
|
|
try:
|
|
run_compose_command(service_dir, ["pull"])
|
|
run_compose_command(service_dir, ["up", "-d"])
|
|
check_container_status(docker_client, service)
|
|
except Exception as e:
|
|
logging.error(f"Failed to update {service}: {e}")
|
|
|
|
# Prune Docker system resources
|
|
try:
|
|
logging.info("Pruning unused containers, images, networks, and volumes...")
|
|
docker_client.containers.prune()
|
|
docker_client.images.prune()
|
|
docker_client.networks.prune()
|
|
docker_client.volumes.prune()
|
|
except Exception as e:
|
|
logging.error(f"Failed to prune system: {e}")
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Update Docker containers using Compose v2 CLI")
|
|
parser.add_argument("-q", "--quiet", dest="stacks", nargs='*', default=DEFAULT_STACKS, help="List of compose services to recreate")
|
|
|
|
args = parser.parse_args()
|
|
stacks = args.stacks
|
|
main(stacks)
|
|
|