[releng] Add block_retired.py

Till Maas till at fedoraproject.org
Mon Sep 29 19:07:01 UTC 2014


commit 3cc16616c7eabf920b68f3da800f700c1279fc2b
Author: Till Maas <opensource at till.name>
Date:   Mon Sep 29 20:47:18 2014 +0200

    Add block_retired.py
    
    Helper script to block (and untag/unblock in build tag for EPEL) all
    retired packages.
    
    Reference: https://fedorahosted.org/rel-eng/ticket/5914

 scripts/block_retired.py |  270 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 270 insertions(+), 0 deletions(-)
---
diff --git a/scripts/block_retired.py b/scripts/block_retired.py
new file mode 100755
index 0000000..2556980
--- /dev/null
+++ b/scripts/block_retired.py
@@ -0,0 +1,270 @@
+#!/usr/bin/python -tt
+# vim: fileencoding=utf8 foldmethod=marker
+# SPDX-License-Identifier: GPL-2.0+
+# {{{ License header: GPLv2+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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, see <https://www.gnu.org/licenses/>.
+# }}}
+
+import argparse
+import datetime
+import getpass
+import logging
+import subprocess
+import time
+
+import koji
+import pkgdb2client
+
+
+log = logging.getLogger(__name__)
+RETIRING_BRANCHES = ["el5", "el6", "epel7", "f21", "master"]
+
+
+class ReleaseMapper(object):
+    BRANCHNAME = 0
+    KOJI_TAG = 1
+    EPEL_BUILD_TAG = 2
+
+    def __init__(self):
+
+        # git branchname, koji tag, pkgdb version
+        self.mapping = (
+            ("master", "f22", ""),
+            ("f21", "f21", ""),
+            ("f20", "f20", ""),
+            ("f19", "f19", ""),
+            ("f18", "f18", ""),
+            ("epel7", "epel7", "epel7-build"),
+            ("el6", "dist-6E-epel", "dist-6E-epel-build"),
+            ("el5", "dist-5E-epel", "dist-5E-epel-build"),
+        )
+
+    def branchname(self, key=""):
+        return self.lookup(key, self.BRANCHNAME)
+
+    def koji_tag(self, key=""):
+        return self.lookup(key, self.KOJI_TAG)
+
+    def epel_build_tag(self, key=""):
+        return self.lookup(key, self.EPEL_BUILD_TAG)
+
+    def lookup(self, key, column):
+        if key:
+            key = key.lower()
+            for row in self.mapping:
+                for c in row:
+                    if c.lower() == key:
+                        return row[column]
+        else:
+            return [row[column] for row in self.mapping]
+        return None
+
+
+def blocked_packages(branch="master"):
+    mapper = ReleaseMapper()
+    tag = mapper.koji_tag(branch)
+    kojisession = koji.ClientSession('https://koji.fedoraproject.org/kojihub')
+    pkglist = kojisession.listPackages(tagID=tag, inherited=True)
+    blocked = [p["package_name"] for p in pkglist if p["blocked"]]
+    return blocked
+
+
+def get_retired_packages(branch="master"):
+    pkgdb = pkgdb2client.PkgDB()
+    retiredresponse = pkgdb.get_packages(
+        "", branches=branch, page="all", status="Retired")
+    retiredinfo = retiredresponse["packages"]
+    retiredpkgs = [p["name"] for p in retiredinfo]
+    return retiredpkgs
+
+
+def pkgdb_retirement_status(package, branch="master"):
+    """ Returns retirement info for `package` in `branch`
+
+    :returns: dict: retired: True - if retired, False if not, None if
+    there was an error, status_change: last status change as datetime object
+    """
+
+    pkgdb = pkgdb2client.PkgDB()
+    retired = None
+    status_change = None
+    try:
+        pkgdbresult = pkgdb.get_package(package, branches=branch)
+        if pkgdbresult["output"] == "ok":
+            for pkginfo in pkgdbresult["packages"]:
+                if pkginfo["package"]["name"] == package:
+                    if pkginfo["status"] == "Retired":
+                        retired = True
+                    else:
+                        retired = False
+                    status_change = datetime.datetime.fromtimestamp(
+                        pkginfo["status_change"])
+                    break
+    except:
+        pass
+
+    return dict(retired=retired, status_change=status_change)
+
+
+def get_retirement_info(message):
+    """ Check whether a message is a retire message.
+
+    :param message: Message to check
+    :returns: (str, str, bool) or (None, None, None): package name and
+    branch the package was retired on, bool: True: package was retired, False:
+        package was unretired
+
+    """
+    if message['topic'] == \
+            u'org.fedoraproject.prod.pkgdb.package.update.status':
+        msg = message['msg']
+        pkgname = msg['package_listing']['package']['name']
+        branch = msg['package_listing']['collection']['branchname']
+        res = dict(name=pkgname, branch=branch)
+        if msg["prev_status"] != "Retired" and msg["status"] == "Retired":
+            res["retired"] = True
+        elif msg["prev_status"] == "Retired" and \
+                msg["status"] != "Retired":
+            res["retired"] = False
+        return res
+    return None
+
+
+def block_package(packages, branch="master"):
+    if isinstance(packages, basestring):
+        packages = [packages]
+
+    if len(packages) == 0:
+        return None
+
+    mapper = ReleaseMapper()
+    tag = mapper.koji_tag(branch)
+    cmd = ["koji", "block-pkg", tag] + packages
+    log.debug("Running: %s", " ".join(cmd))
+    subprocess.check_call(cmd)
+
+    epel_build_tag = mapper.epel_build_tag(branch)
+
+    if epel_build_tag:
+        cmd = ["koji", "untag-build", "--all", tag] + packages
+        log.debug("Running: %s", " ".join(cmd))
+        subprocess.check_call(cmd)
+
+        cmd = ["koji", "unblock-pkg", epel_build_tag] + packages
+        log.debug("Running: %s", " ".join(cmd))
+        subprocess.check_call(cmd)
+
+
+def handle_message(message, retiring_branches=RETIRING_BRANCHES):
+    messageinfo = get_retirement_info(message)
+    msg_id = message["msg_id"]
+    if messageinfo is None:
+        return None
+
+    if messageinfo["retired"] is False:
+        return False
+
+    branch = messageinfo["branch"]
+    if branch not in retiring_branches:
+        log.error("Message '%s' for the wrong branch '%s'", msg_id,
+                  branch)
+        return None
+
+    package = messageinfo["name"]
+
+    pkgdbinfo = pkgdb_retirement_status(package, branch)
+
+    if pkgdbinfo["retired"] is not True:
+        log.error("Processing '%s', package '%s' not retired",
+                  msg_id, package)
+
+    log.debug("'%s' retired on '%s'", package, pkgdbinfo["status_change"])
+    return block_package(package, branch)
+
+
+def block_all_retired(branches=RETIRING_BRANCHES):
+    for branch in branches:
+        log.debug("Processing branch %s", branch)
+        retired = get_retired_packages(branch)
+        blocked = blocked_packages(branch)
+
+        unblocked = []
+        for pkg in retired:
+            if pkg not in blocked:
+                unblocked.append(pkg)
+
+        if unblocked:
+            log.info("Blocked packages %s on %s", unblocked, branch)
+            block_package(unblocked, branch)
+
+
+class SubjectSMTPHandler(logging.handlers.SMTPHandler):
+    # Class copied from autosigner.py
+    subject_prefix = ""
+
+    def getSubject(self, record):
+        first_line = record.message.split("\n")[0]
+        fmt = self.subject_prefix + "{0.levelname}: {first_line}"
+
+        return fmt.format(record, first_line=first_line)
+
+
+def setup_logging(debug=False, mail=False):
+    if debug:
+        log.setLevel(logging.DEBUG)
+    else:
+        log.setLevel(logging.INFO)
+
+    formatter = logging.Formatter(
+        '%(asctime)s: %(levelname)s: %(message)s',
+    )
+    # Log in UTC
+    formatter.converter = time.gmtime
+
+    console_logger = logging.StreamHandler()
+    if debug:
+        console_logger.setLevel(logging.DEBUG)
+    else:
+        console_logger.setLevel(logging.INFO)
+    console_logger.setFormatter(formatter)
+    log.addHandler(console_logger)
+
+    if mail:
+        # FIXME: Make this a config option
+        fedora_user = getpass.getuser()
+        mail_logger = SubjectSMTPHandler(
+            "127.0.0.1", fedora_user, [fedora_user], "block_retired event")
+        if debug:
+            mail_logger.setLevel(logging.DEBUG)
+        mail_logger.setFormatter(formatter)
+        mail_logger.subject_prefix = "Package Blocker: "
+        log.addHandler(mail_logger)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser("Block retired packages")
+    parser.add_argument("--debug", default=False, action="store_true")
+    parser.add_argument("packages", nargs="*", metavar="package",
+                        help="Packages to block, default all retired packages")
+    parser.add_argument(
+        "--branch", default="master",
+        help="Branch to retire specified packages on, default: %(default)s")
+    args = parser.parse_args()
+
+    setup_logging(args.debug)
+
+    if not args.packages:
+        block_all_retired()
+    else:
+        block_package(args.packages, args.branch)


More information about the rel-eng mailing list