From 9f57a53f6d6ae41580b46c75a7a4cf1d6bf376e0 Mon Sep 17 00:00:00 2001 From: Jindrich Luza Date: May 25 2015 15:08:43 +0000 Subject: container-build support --- diff --git a/src/pyrpkg/__init__.py b/src/pyrpkg/__init__.py index 97b1171..779f4cc 100644 --- a/src/pyrpkg/__init__.py +++ b/src/pyrpkg/__init__.py @@ -39,7 +39,9 @@ except ImportError: pass from pyrpkg.sources import HashtypeMixingError, SourcesFile - +from osbs.api import OSBS +from osbs.conf import Configuration +from osbs.build import BuildResponse # Define our own error class class rpkgError(Exception): @@ -57,6 +59,10 @@ class NullHandler(logging.Handler): pass +class UnknownTargetError(Exception): + faultCode = 1004 + + h = NullHandler() # This is our log object, clients of this library can use this object to # define their own logging needs @@ -2415,6 +2421,98 @@ class Commands(object): # Run the command self._run_command(cmd, shell=True) + def osbs_build(self, config_file, config_section, target_override=False, + yum_repourls=[]): + os_conf = Configuration(conf_file=config_file, conf_section=config_section) + build_conf = Configuration(conf_file=config_file, conf_section=config_section) + osbs = OSBS(os_conf, build_conf) + + git_uri = re.sub(r"^git\+ssh", "git", self.push_url) + git_uri = re.sub("^ssh", "git", git_uri) + git_uri = re.sub("[^/]+@", "", git_uri) + git_ref = self.branch_merge + user = self.user + component = self.module_name + docker_target = self.target + if not target_override: + # Translate the build target into a docker target, + # but only if --target wasn't specified on the command-line + docker_target = '%s-docker-candidate' % self.target.split('-candidate')[0] + + build = osbs.create_build( + git_uri=git_uri, + git_ref=git_ref, + user=user, + component=component, + target=docker_target, + architecture="x86_64", + yum_repourls=yum_repourls + ) + build_id = build.build_id + print("Build submitted (%s), watching logs (feel free to interrupt)" % build_id) + for line in osbs.get_build_logs(build_id, follow=True): + print(line) + build_response = osbs.wait_for_build_to_finish(build_id) + if build_response.is_succeeded(): + repositories = build_response.get_repositories() + if repositories: + image_names = repositories.get("primary", []) + repositories.get("unique", []) + print("You can pull the image with one of the following commands:") + for image in image_names: + print(" docker pull %s" % image) + else: + raise RuntimeError("Build '%s' wasn't processed correctly. Please, report this." % build_id) + else: + raise RuntimeError("Build has failed.") + + def container_build_koji(self, target_override=False, opts={}, + kojiconfig=None, build_client=None, + koji_task_watcher=None): + + docker_target = self.target + if not target_override: + # Translate the build target into a docker target, + # but only if --target wasn't specified on the command-line + docker_target = '%s-docker-candidate' % self.target.split('-candidate')[0] + + koji_session_backup = (self.build_client, self.kojiconfig) + (self.build_client, self.kojiconfig) = (build_client, kojiconfig) + try: + self.load_kojisession() + if "buildContainer" not in self.kojisession.system.listMethods(): + raise RuntimeError("Kojihub instance does not support buildContainer") + + build_target = self.kojisession.getBuildTarget(docker_target) + if not build_target: + msg = "Unknown build target: %s" % docker_target + self.log.error(msg) + raise UnknownTargetError(msg) + else: + dest_tag = self.kojisession.getTag(build_target['dest_tag']) + if not dest_tag: + self.log.error("Unknown destination tag: %s" % + build_target['dest_tag_name']) + if dest_tag['locked'] and not build_opts.scratch: + self.log.error("Destination tag %s is locked" % dest_tag['name']) + + source = self.anongiturl % {"module":self.module_name} + source += "#%s" % self.commithash + + task_opts = {} + for key in ('scratch', 'name', 'version', 'release'): + if key in opts: + task_opts[key] = opts[key] + priority = opts.get("priority", None) + task_id = self.kojisession.buildContainer(source, + docker_target, + task_opts, + priority=priority) + koji_task_watcher(self.kojisession, [task_id]) + except Exception: + raise + finally: + (self.build_client, self.kojiconfig) = koji_session_backup + self.load_kojisession() class GitIgnore(object): """ Smaller wrapper for managing a .gitignore file and it's entries. """ diff --git a/src/pyrpkg/cli.py b/src/pyrpkg/cli.py index 3df0b59..37fbf24 100755 --- a/src/pyrpkg/cli.py +++ b/src/pyrpkg/cli.py @@ -23,6 +23,7 @@ import xmlrpclib import pwd import koji +OSBS_DEFAULT_CONF_FILE = "/etc/osbs/osbs.conf" class cliClient(object): """This is a client class for rpkg clients.""" @@ -203,6 +204,7 @@ class cliClient(object): self.register_clone() self.register_commit() self.register_compile() + self.register_container_build() self.register_diff() self.register_gimmespec() self.register_gitbuildhash() @@ -797,6 +799,41 @@ defined, packages will be built sequentially.""" % {'name': self.name}) 'verrel', help='Print the name-version-release') verrel_parser.set_defaults(command=self.verrel) + def register_container_build(self): + self.container_build_parser = \ + self.subparsers.add_parser('container-build', + help='build a container') + osbs_group = self.container_build_parser.add_argument_group('osbs') + osbs_group.add_argument('--osbs-config', + help="path to file with configuration of osbs", + metavar="PATH", + default=OSBS_DEFAULT_CONF_FILE) + osbs_group.add_argument('--instance', + help=("use specific instance specified " + "by section name in config"), + metavar="SECTION", default="default") + osbs_group.add_argument('--repo-url', + help=("URL of yum repo file"), + nargs='*') + koji_group = self.container_build_parser.add_argument_group('koji') + + self.container_build_parser.add_argument('--target', + help='Override the default target', + default=None) + self.container_build_parser.add_argument('--scratch', + help='Scratch build', + action="store_true") + + self.container_build_parser.add_argument('--build-with', + help='Build container with ' + 'specified builder type. Default ' + 'is koji', + dest="build_with", + choices=("koji", "osbs"), + default="koji") + + self.container_build_parser.set_defaults(command=self.container_build) + # All the command functions go here def usage(self): self.parser.print_help() @@ -962,6 +999,61 @@ defined, packages will be built sequentially.""" % {'name': self.name}) self.cmd.compile(arch=arch, short=short, builddir=self.args.builddir) + def container_build(self): + if self.args.build_with == "koji": + self.container_build_koji() + elif self.args.build_with == "osbs": + self.container_build_osbs() + + def container_build_koji(self): + target_override = False + # Override the target if we were supplied one + if self.args.target: + self.cmd._target = self.args.target + target_override = True + + opts = {"scratch": self.args.scratch, + "quiet": self.args.q} + + section_name = "%s.container-build" % self.name + err_msg = "Missing {option} option in [{plugin.section}] section. "\ + "Using {option} from [{root.section}]" + err_args = {"plugin.section": section_name, "root.section": self.name} + + if self.config.has_option(section_name, "kojiconfig"): + kojiconfig=self.config.get(section_name, "kojiconfig") + else: + err_args["option"] = "kojiconfig" + self.log.debug(err_msg % err_args) + kojiconfig=self.config.get(self.name, "kojiconfig") + + if self.config.has_option(section_name, "build_client"): + build_client=self.config.get(section_name, "build_client") + else: + err_args["option"] = "kojiconfig" + self.log.debug(err_msg % err_args) + build_client=self.config.get(self.name, "build_client") + + self.cmd.container_build_koji(target_override, opts=opts, + kojiconfig=kojiconfig, + build_client=build_client, + koji_task_watcher=self._watch_koji_tasks) + + def container_build_osbs(self): + target_override = False + # Override the target if we were supplied one + if self.args.target: + self.cmd._target = self.args.target + target_override = True + + self.cmd.osbs_build( + config_file=self.args.osbs_config, + config_section=self.args.instance, + target_override=target_override, + yum_repourls=self.args.repo_url + ) + + def diff(self): self.cmd.diff(self.args.cached, self.args.files) diff --git a/src/rpkg b/src/rpkg index e3b71c7..b222e54 100755 --- a/src/rpkg +++ b/src/rpkg @@ -11,6 +11,7 @@ # the full text of the license. import pyrpkg +import pyrpkg.cli import os import sys import logging