#!/usr/bin/python3 """ Icinga/Nagios plugin to check Forgejo instance upgrade status. Copyright (c) 2023 Benjamin Renard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ import argparse import logging import re import subprocess # nosec import sys import requests import xmltodict parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", action="store_true") parser.add_argument("-p", "--path", type=str, help="Forgejo bin path", default="forgejo") parser.add_argument( "-U", "--url", type=str, help="Forgejo releases RSS URL", default="https://forgejo.org/releases/rss.xml", ) parser.add_argument( "--rc", action="store_true", dest="include_rc", help="Allow release candidate (default: only stable release are considered)", ) parser.add_argument( "-t", "--timeout", type=int, help="Specify timeout for HTTP requests (default: 20)", default=20 ) options = parser.parse_args() logging.basicConfig(level=logging.DEBUG if options.debug else logging.WARNING) CURRENT = None cmd = [options.path, "--version"] logging.debug("Command use to retrieve current version of Forgejo: %s", " ".join(cmd)) OUTPUT = None EXCEPTION = None try: OUTPUT = subprocess.check_output(cmd) # nosec logging.debug("Output:\n%s", OUTPUT) m = re.search("version ([^ ]+) built", OUTPUT.decode("utf8", errors="ignore")) if m: CURRENT = m.group(1) except Exception as err: # pylint: disable=broad-except EXCEPTION = err logging.debug("Current version: %s", CURRENT) if not CURRENT: print("UNKNOWN - Fail to retrieve current Forgejo") print(f'Command: {" ".join(cmd)}') print("Output:") print(OUTPUT if OUTPUT else "") print("Exception:") print(EXCEPTION if EXCEPTION else "") sys.exit(3) CURRENT = CURRENT.replace("+", "-") logging.debug("Cleaned current version: %s", CURRENT) LATEST = None LATEST_INT = None try: logging.debug("Get releases RSS feed from %s...", options.url) r = requests.get(options.url, timeout=options.timeout) logging.debug("Data retrieve:\n%s", r.text) data = xmltodict.parse(r.text) versions = {} for item in data["rss"]["channel"]["item"]: version = re.sub("^v", "", item["title"]) if not options.include_rc and "-rc" in version: logging.debug("Ignore release candidate %s", version) continue version_int = int(re.sub(r"[\.-]", "000", version)) logging.debug("Found version %s (%s)", version, version_int) if not LATEST_INT or LATEST_INT < version_int: if LATEST: logging.debug( "Version %s considered as newer than %s, override latest version", version, LATEST, ) LATEST = version LATEST_INT = version_int else: logging.debug("Version %s considered as oldest than %s", version, LATEST) except Exception: # pylint: disable=broad-except # nosec pass logging.debug("Latest version: %s", LATEST) if not LATEST: print("UNKNOWN - Fail to retrieve latest Forgejo release from the project RSS feed") print(f"Current version: {CURRENT}") sys.exit(3) if LATEST == CURRENT: print(f"OK - The latest release of Forgejo is currently used ({LATEST})") sys.exit(0) print( "WARNING - The version of Forgejo currently used is not the latest " f"({CURRENT} vs {LATEST})" ) sys.exit(1)