From cd8c20255e92a1ec0842d5b47063588cd1ddbbc8 Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Thu, 1 Feb 2024 11:15:12 +0100 Subject: [PATCH] Add upgrade feature --- README.md | 19 +++++++++-- check_woodpecker_upgrade | 72 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e3ea13d..a8257f1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ This Icinga/Nagios check plugin permit to check Woodpecker CI instance upgrade status by comparing the local woodpecker-server binary version against the latest stable release. +This script could also be used to easily upgrade your installation. It could automatically: + +- Download latest release asset matching to your already installed woodpecker packages +- Install corresponding packages +- Restart corresponding active woodpecker services (agent & server). Note: systemd services name must match the woodpecker package name. + ## Installation ``` @@ -16,17 +22,24 @@ service nagios-nrpe-server reload ## Usage ``` -usage: check_woodpecker_upgrade [-h] [-d] [-p PATH] [-U URL] [--pre-release] [--draft] [-t TIMEOUT] +usage: check_woodpecker_upgrade [-h] [-d] [-v] [-p PATH] [-U URL] [--pre-release] + [--draft] [-t TIMEOUT] [-u] [-f] [--arch ARCH] options: -h, --help show this help message and exit -d, --debug + -v, --verbose -p PATH, --path PATH Woodpecker CI bin path -U URL, --url URL Woodpecker CI releases URL - --pre-release Allow pre-release (default: only stable release are considered) - --draft Allow draft release (default: only stable release are considered) + --pre-release Allow pre-release (default: only stable release are + considered) + --draft Allow draft release (default: only stable release are + considered) -t TIMEOUT, --timeout TIMEOUT Specify timeout for HTTP requests (default: 20) + -u, --upgrade Upgrade Woodpecker CI + -f, --force Force upgrade Woodpecker CI + --arch ARCH System dpkg architecture (default: auto-detect) ``` ## Copyright diff --git a/check_woodpecker_upgrade b/check_woodpecker_upgrade index fdf0952..63519d1 100755 --- a/check_woodpecker_upgrade +++ b/check_woodpecker_upgrade @@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import argparse import logging +import os import re import subprocess import sys @@ -30,6 +31,7 @@ import requests parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", action="store_true") +parser.add_argument("-v", "--verbose", action="store_true") parser.add_argument( "-p", "--path", type=str, help="Woodpecker CI bin path", default="woodpecker-server" ) @@ -53,10 +55,15 @@ parser.add_argument( parser.add_argument( "-t", "--timeout", type=int, help="Specify timeout for HTTP requests (default: 20)", default=20 ) +parser.add_argument("-u", "--upgrade", action="store_true", help="Upgrade Woodpecker CI") +parser.add_argument("-f", "--force", action="store_true", help="Force upgrade Woodpecker CI") +parser.add_argument("--arch", help="System dpkg architecture (default: auto-detect)") options = parser.parse_args() -logging.basicConfig(level=logging.DEBUG if options.debug else logging.WARNING) +logging.basicConfig( + level=logging.DEBUG if options.debug else (logging.INFO if options.verbose else logging.WARNING) +) CURRENT = None @@ -114,13 +121,74 @@ if LATEST is None: logging.debug("Latest version is %s", LATEST["name"]) -if LATEST["name"] == CURRENT: +if LATEST["name"] == CURRENT and not (options.upgrade and options.force): print( f"OK - The latest release of Woodpecker CI is currently used " f"({LATEST['name']}, published on {LATEST['published_at']})" ) sys.exit(0) +if options.upgrade: + if not options.arch: + logging.info("Auto-detect dpkg architecture...") + options.arch = ( + subprocess.check_output(["dpkg-architecture", "--query", "DEB_HOST_ARCH"]) + .decode("utf8") + .strip() + ) + logging.info("Auto-detected dpkg architecture: %s", options.arch) + + logging.info("List installed Woodpecker package...") + INSTALLED = ( + subprocess.check_output(["dpkg-query", "-Wf", "${Package}\\n", "woodpecker-*"]) + .decode("utf8") + .strip() + .split("\n") + ) + logging.info("Installed Woodpecker packages: %s", ", ".join(INSTALLED)) + + for PACKAGE in INSTALLED: + PACKAGE_ASSET = None + for asset in LATEST["assets"]: + if re.match( + r"^" + PACKAGE + r"_" + LATEST["name"] + "_" + options.arch + r"\.deb$", + asset["name"], + ): + PACKAGE_ASSET = asset + break + if not PACKAGE_ASSET: + logging.warning("No asset found for package %s", PACKAGE) + continue + + PATH = f"/tmp/{PACKAGE_ASSET['name']}" + logging.info( + "Download package %s %s to %s (%s)", + PACKAGE, + LATEST["name"], + PATH, + PACKAGE_ASSET["browser_download_url"], + ) + r = requests.get(PACKAGE_ASSET["browser_download_url"], timeout=options.timeout) + with open(PATH, "wb") as fp: + fp.write(r.content) + + logging.info("Install package %s %s", PACKAGE, LATEST["name"]) + subprocess.run(["dpkg", "-i", PATH], check=True) + + logging.info("Remove temporary file") + os.remove(PATH) + + STATUS = subprocess.run( + ["systemctl", "is-active", PACKAGE], check=False, capture_output=True + ) + if STATUS.stdout.decode().strip() == "active": + logging.info("Service %s is active, restart it", PACKAGE) + subprocess.run(["systemctl", "restart", PACKAGE], check=True) + else: + logging.info("No service %s is running", PACKAGE) + + sys.exit(0) + print( "WARNING - The version of Woodpecker CI currently used is not the latest " f"('{CURRENT}' vs '{LATEST['name']}', published on {LATEST['published_at']})"