From ee59511e5d594b5657f8a64b0277416e206f7b61 Mon Sep 17 00:00:00 2001 From: vvzvlad Date: Sat, 2 Aug 2025 01:15:49 +0300 Subject: [PATCH] add docker image check and update functionality to rotate.py --- rotate/rotate.py | 92 ++++++++++++++++++++++++++++++++++++++++++++++-- rotate/run.sh | 7 ++++ 2 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 rotate/run.sh diff --git a/rotate/rotate.py b/rotate/rotate.py index fa65f40..b8c9c7a 100644 --- a/rotate/rotate.py +++ b/rotate/rotate.py @@ -1,7 +1,20 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + # flake8: noqa # pylint: disable=broad-exception-raised, raise-missing-from, too-many-arguments, redefined-outer-name -# pylance: disable=reportMissingImports, reportMissingModuleSource, reportGeneralTypeIssues -# type: ignore +# pylint: disable=multiple-statements, logging-fstring-interpolation, trailing-whitespace, line-too-long +# pylint: disable=broad-exception-caught, missing-function-docstring, missing-class-docstring +# pylint: disable=f-string-without-interpolation, wrong-import-position +# pylance: disable=reportMissingImports, reportMissingModuleSource + + +# +#curl https://gitea.vvzvlad.xyz/vvzvlad/nexus/raw/branch/main/rotate/grist.json?token=7b58d730732f07e90a88169a63531d96bfc0fbbd -o grist.json; +#curl https://gitea.vvzvlad.xyz/vvzvlad/nexus/raw/branch/main/rotate/requirements.txt?token=7b58d730732f07e90a88169a63531d96bfc0fbbd -o requirements.txt; +#pip3 install -r requirements.txt --break-system-packages; +#docker pull nexusxyz/nexus-cli:latest +# curl https://gitea.vvzvlad.xyz/vvzvlad/nexus/raw/branch/main/rotate/rotate.py?token=7b58d730732f07e90a88169a63531d96bfc0fbbd -o rotate.py; python3 rotate.py; import re from datetime import datetime, timedelta, timezone @@ -162,6 +175,76 @@ def get_next_node(grist, logger): return None +def check_and_update_docker_image(image_name, logger): + """Check for new docker image and update if available""" + try: + logger.info(f"Checking for updates for image: {image_name}") + + # Get local image digest + local_digest_cmd = f"docker images --digests --format '{{{{.Digest}}}}' {image_name}" + local_result = subprocess.run(local_digest_cmd, shell=True, capture_output=True, text=True, timeout=30) + local_digest = local_result.stdout.strip() if local_result.returncode == 0 else "" + + # Get remote image digest without pulling + remote_digest_cmd = f"docker manifest inspect {image_name} --verbose | grep -m1 '\"digest\"' | cut -d'\"' -f4" + remote_result = subprocess.run(remote_digest_cmd, shell=True, capture_output=True, text=True, timeout=60) + + if remote_result.returncode != 0: + logger.warning(f"Failed to get remote digest for {image_name}, proceeding with pull check") + # Fallback to pull and check output + pull_cmd = f"docker pull {image_name}" + pull_result = subprocess.run(pull_cmd, shell=True, capture_output=True, text=True, timeout=300) + + if pull_result.returncode == 0: + if "Image is up to date" in pull_result.stderr or "up to date" in pull_result.stdout: + logger.info(f"Image {image_name} is already up to date") + else: + logger.info(f"Image {image_name} updated successfully") + return True + else: + logger.error(f"Failed to pull image {image_name}: {pull_result.stderr.strip()}") + return False + + remote_digest = remote_result.stdout.strip() + + # Compare digests + if local_digest and remote_digest: + if local_digest == remote_digest: + logger.info(f"Image {image_name} is already up to date (digest: {local_digest[:19]}...)") + return True + else: + logger.info(f"New version available for {image_name}, downloading...") + # Pull new image + pull_cmd = f"docker pull {image_name}" + pull_result = subprocess.run(pull_cmd, shell=True, capture_output=True, text=True, timeout=300) + + if pull_result.returncode == 0: + logger.info(f"Image {image_name} updated successfully (new digest: {remote_digest[:19]}...)") + return True + else: + logger.error(f"Failed to pull updated image {image_name}: {pull_result.stderr.strip()}") + return False + else: + logger.info(f"Unable to compare digests, performing full pull for {image_name}") + # Pull image + pull_cmd = f"docker pull {image_name}" + pull_result = subprocess.run(pull_cmd, shell=True, capture_output=True, text=True, timeout=300) + + if pull_result.returncode == 0: + logger.info(f"Image {image_name} pulled successfully") + return True + else: + logger.error(f"Failed to pull image {image_name}: {pull_result.stderr.strip()}") + return False + + except subprocess.TimeoutExpired: + logger.error(f"Timeout while checking/updating image {image_name}") + return False + except Exception as e: + logger.error(f"Failed to check/update image {image_name}: {str(e)}") + return False + + def main_rotation_cycle(): """Main rotation cycle for nexus nodes""" colorama.init(autoreset=True) @@ -172,6 +255,11 @@ def main_rotation_cycle(): ch.setFormatter(formatter) logger.addHandler(ch) + # Check and update docker image + image_name = "nexusxyz/nexus-cli:latest" + if not check_and_update_docker_image(image_name, logger): + logger.warning(f"Failed to update {image_name}, continuing with existing image") + # Load grist configuration try: with open('grist.json', 'r', encoding='utf-8') as f: diff --git a/rotate/run.sh b/rotate/run.sh new file mode 100644 index 0000000..5b861d8 --- /dev/null +++ b/rotate/run.sh @@ -0,0 +1,7 @@ + +curl https://gitea.vvzvlad.xyz/vvzvlad/nexus/raw/branch/main/rotate/rotate.py?token=7b58d730732f07e90a88169a63531d96bfc0fbbd -o rotate.py; +curl https://gitea.vvzvlad.xyz/vvzvlad/nexus/raw/branch/main/rotate/grist.json?token=7b58d730732f07e90a88169a63531d96bfc0fbbd -o grist.json; +curl https://gitea.vvzvlad.xyz/vvzvlad/nexus/raw/branch/main/rotate/requirements.txt?token=7b58d730732f07e90a88169a63531d96bfc0fbbd -o requirements.txt; +pip3 install -r requirements.txt --break-system-packages; +docker pull nexusxyz/nexus-cli:latest +screen -S rotate -m python3 rotate.py