Esempio n. 1
0
 def setup_logging2(self, environ):
     """Adjust logging based on configuration options"""
     opts = self.options
     # determine log level
     level = opts['LogLevel']
     valid_levels = ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
     # the config value can be a single level name or a series of
     # logger:level names pairs. processed in order found
     default = None
     for part in level.split():
         pair = part.split(':', 1)
         if len(pair) == 2:
             name, level = pair
         else:
             name = 'koji'
             level = part
             default = level
         if level not in valid_levels:
             raise koji.GenericError("Invalid log level: %s" % level)
         # all our loggers start with koji
         if name == '':
             name = 'koji'
             default = level
         elif name.startswith('.'):
             name = 'koji' + name
         elif not name.startswith('koji'):
             name = 'koji.' + name
         level_code = logging.getLevelName(level)
         logging.getLogger(name).setLevel(level_code)
     logger = logging.getLogger("koji")
     # if KojiDebug is set, force main log level to DEBUG
     if opts.get('KojiDebug'):
         logger.setLevel(logging.DEBUG)
     elif default is None:
         # LogLevel did not configure a default level
         logger.setLevel(logging.WARNING)
     self.formatter = HubFormatter(opts['LogFormat'])
     self.formatter.environ = environ
     self.log_handler.setFormatter(self.formatter)
Esempio n. 2
0
    def getTag(self, taginfo, **kw):
        """
        Retrieve the given tag from koji.

        Args:
            taginfo (int or basestring): The tag you want info about.
            strict (bool): If True, raise an Exception if epel tags are queried. Defaults to False.
        Returns:
            dict or None: A dictionary of tag information, or None if epel is requested and strict
                is False.
        Raises:
            koji.GenericError: If strict is True and epel is requested.
        """
        for nr in self.__tags__:
            if taginfo == self.__tags__[nr][0]:
                toreturn = self.__tags__[nr][1]
                toreturn['id'] = nr
                return toreturn

        if isinstance(taginfo, int):
            taginfo = "f%d" % taginfo

        if taginfo.startswith("epel"):
            if kw.get("strict", False):
                raise koji.GenericError("Invalid tagInfo: '%s'" % taginfo)

            else:
                return None

        return {
            'maven_support': False,
            'locked': False,
            'name': taginfo,
            'perm': None,
            'id': 246,
            'arches': None,
            'maven_include_all': False,
            'perm_id': None
        }
Esempio n. 3
0
 def test_canfail_canceled(self):
     """Canceled canfail tasks should not raise exceptions"""
     parent = 1
     task_ids = [5, 6, 7]
     canfail = [7]
     for t in task_ids:
         task = self.getTask(t)
         task.getResult.return_value = "OK"
         task.isCanceled.return_value = False
     self.tasks[7].getResult.side_effect = koji.GenericError('canceled')
     self.tasks[7].isCanceled.return_value = True
     results = self.host_exports.taskWaitResults(parent,
                                                 task_ids,
                                                 canfail=canfail)
     expect_f = {
         'faultCode': koji.GenericError.faultCode,
         'faultString': 'canceled'
     }
     expect = [[5, "OK"], [6, "OK"], [7, expect_f]]
     self.assertEqual(results, expect)
     self.host_exports.taskUnwait.assert_called_with(parent)
     self.assertEqual(self.queries, [])
Esempio n. 4
0
 def test_BaseTaskHandler_wait_some_not_done_all_set_failany_set_failed_task(
         self):
     """ Tests that the wait function raises an exception when one of the subtask fails when the failany flag is set
     to True.
     """
     temp_path = get_tmp_dir_path('TestTask')
     obj = TestTask(12345678, 'some_method', ['random_arg'], None, None,
                    temp_path)
     makedirs(temp_path)
     obj.session = Mock()
     obj.session.host.taskSetWait.return_value = None
     obj.session.host.taskWait.side_effect = [[[1551234], [1591234]],
                                              [[1551234, 1591234], []]]
     obj.session.getTaskResult.side_effect = koji.GenericError(
         'Uh oh, we\'ve got a problem here!')
     try:
         obj.wait([1551234, 1591234], all=True, failany=True)
         raise Exception('A GeneralError was not raised.')
     except koji.GenericError as e:
         self.assertEquals(e.args[0], 'Uh oh, we\'ve got a problem here!')
         obj.session.host.taskSetWait.assert_called_once_with(
             12345678, [1551234, 1591234])
Esempio n. 5
0
    def compose_create(self, nvr: NVR, distro: str, images: List[ImageRequest],
                       kojidata: ComposeRequest.Koji):
        url = urllib.parse.urljoin(self.url, f"/compose")
        req = urllib.request.Request(url)
        cro = ComposeRequest(nvr, distro, images, kojidata)
        dat = json.dumps(cro.as_dict())
        raw = dat.encode('utf-8')
        req = urllib.request.Request(url, raw)
        req.add_header('Content-Type', 'application/json')
        req.add_header('Content-Length', len(raw))

        try:
            with urllib.request.urlopen(req, raw) as res:
                payload = res.read().decode('utf-8')
        except urllib.error.HTTPError as e:
            body = e.read().decode('utf-8').strip()
            msg = f"Failed to create the compose request: {body}"
            raise koji.GenericError(msg) from None

        ps = json.loads(payload)
        compose_id, koji_build_id = ps["id"], ps["koji_build_id"]
        return compose_id, koji_build_id
Esempio n. 6
0
    def test_handle_save_failed_tree_errors(self, watch_tasks_mock,
                                            activate_session_mock, stdout):
        # koji save-failed-tree 123 456
        arguments = [123, 456]
        options = mock.MagicMock()
        self.parser.parse_args.return_value = [options, arguments]
        self.parser.error.side_effect = Exception()
        self.session.getAPIVersion.return_value = koji.API_VERSION
        self.session.listBuildroots.return_value = [{'id': 321}]

        self.assertRaises(Exception, cli.handle_save_failed_tree, self.options,
                          self.session, self.args)

        arguments = ["text"]
        self.parser.parse_args.return_value = [options, arguments]
        self.assertRaises(Exception, cli.handle_save_failed_tree, self.options,
                          self.session, self.args)
        cli.logger = mock.MagicMock()

        # plugin not installed
        arguments = [123]
        self.parser.parse_args.return_value = [options, arguments]
        self.session.saveFailedTree.side_effect = koji.GenericError(
            "Invalid method")
        cli.handle_save_failed_tree(self.options, self.session, self.args)
        actual = stdout.getvalue()
        self.assertTrue(
            'The save_failed_tree plugin appears to not be installed' in
            actual)

        # Task which is not FAILED, disabled in config, wrong owner
        self.session.saveFailedTree.side_effect = koji.PreBuildError(
            'placeholder')
        with self.assertRaises(koji.PreBuildError) as cm:
            cli.handle_save_failed_tree(self.options, self.session, self.args)
        e = cm.exception
        self.assertEqual(e, self.session.saveFailedTree.side_effect)
Esempio n. 7
0
    def test_handle_import_src_rpm_import_with_no_exist_build(self):
        """Test handle_import source RPM import.
           No build is on the server case,
           this tests are focusing on do_import() function coverage.
        """
        arguments = ['/path/to/bash-4.4.12-5.fc26.src.rpm', '--src-epoch', 'None']
        options = mock.MagicMock()
        session = mock.MagicMock()

        # No exist build test case
        # import general case
        session.getBuild.return_value = None
        session.getRPM.return_value = None
        expected = "uploading %s... done\n" % arguments[0]
        expected += "importing %s... done\n" % arguments[0]
        self.__do_import_test(
            options, session, arguments,
            rpm_header=self.srpm_header, expected=expected)

        # import error case
        session.importRPM.side_effect = koji.GenericError('fake-import-error')
        expected = "uploading %s... done\n" % arguments[0]
        expected += "importing %s... \n" % arguments[0]
        expected += "Error importing: fake-import-error\n"
        self.__do_import_test(
            options, session, arguments,
            rpm_header=self.srpm_header, expected=expected)

        # import with --link (hardlink) option
        session.importRPM.side_effect = None
        expected = "importing %s... done\n" % arguments[0]
        self.__do_import_test(
            options, session, arguments + ['--link'],
            upload_rpm_mock=mock.patch('koji_cli.commands.linked_upload').start(),
            rpm_header=self.srpm_header, expected=expected)
        session.uploadWrapper.assert_not_called()
Esempio n. 8
0
    def cleanupTask(self, task_id, wait=True):
        """Clean up after task

        - kill children
        - expire session

        Return True if all children were successfully killed, False otherwise.
        """
        pid = self.pids.get(task_id)
        if not pid:
            raise koji.GenericError("No pid for task %i" % task_id)
        children = self._childPIDs(pid)
        if children:
            # send SIGINT once to let mock mock try to clean up
            self._killChildren(task_id, children, sig=signal.SIGINT, pause=3.0)
        if children:
            self._killChildren(task_id, children)
        if children:
            self._killChildren(task_id, children, sig=signal.SIGKILL, timeout=3.0)

        #expire the task's subsession
        session_id = self.subsessions.get(task_id)
        if session_id:
            self.logger.info("Expiring subsession %i (task %i)" % (session_id, task_id))
            try:
                self.session.logoutChild(session_id)
                del self.subsessions[task_id]
            except:
                #not much we can do about it
                pass
        if wait:
            return self._waitTask(task_id, pid)
        else:
            # task has already been waited on, and we've cleaned
            # up as much as we can
            return True
Esempio n. 9
0
def rmtree(path, logger=None):
    """Delete a directory tree without crossing fs boundaries"""
    # implemented to avoid forming long paths
    # see: https://pagure.io/koji/issue/201
    logger = logger or logging.getLogger('koji')
    try:
        st = os.lstat(path)
    except FileNotFoundError:
        logger.warning("No such file/dir %s for removal" % path)
        return
    if not stat.S_ISDIR(st.st_mode):
        raise koji.GenericError("Not a directory: %s" % path)
    dev = st.st_dev
    cwd = os.getcwd()
    root = os.path.abspath(path)
    try:
        try:
            os.chdir(path)
        except OSError as e:
            if e.errno not in (errno.ENOENT, errno.ESTALE):
                return
            raise
        _rmtree(dev, root)
    finally:
        os.chdir(cwd)
    try:
        os.rmdir(path)
    except OSError as e:
        if e.errno == errno.ENOTEMPTY:
            logger.warning(
                '%s path is not empty, but it may be a phantom error caused by some'
                ' race condition',
                path,
                exc_info=True)
        elif e.errno != errno.ENOENT:
            raise
Esempio n. 10
0
 def __init__(self, str):
     super(HasTest, self).__init__(str)
     try:
         self.field = str.split()[1]
     except IndexError:
         raise koji.GenericError("Invalid or missing field in policy test")
Esempio n. 11
0
 def get_test_handler(self, str):
     name = str.split(None, 1)[0]
     try:
         return self.tests[name](str)
     except KeyError:
         raise koji.GenericError("missing test handler: %s" % name)
Esempio n. 12
0
 def handler(self, host):
     #note: this is a foreground task
     if host['id'] != self.session.host.getID():
         raise koji.GenericError("Host mismatch")
     self.manager.restart_pending = True
     return "graceful restart initiated"
Esempio n. 13
0
 def handler(self, *args, **opts):
     raise koji.GenericError("Invalid method: %s" % self.method)
Esempio n. 14
0
def get_tasks(session, parser, opts):
    channels = opts.channels
    hosts = opts.hosts
    methods = opts.methods
    states = getattr(opts, 'states', None)
    limit = getattr(opts, 'limit', None)
    offset = getattr(opts, 'offset', None)
    channel_ids = []
    if channels:
        for channel in channels:
            channel_ids.append(session.getChannel(channel, strict=True)['id'])
    host_ids = []
    if hosts:
        for host in hosts:
            host_ids.append(session.getHost(host, strict=True)['id'])
    state_nums = []
    for state in states:
        if isinstance(state, six.integer_types):
            if 0 <= state <= 5:
                state_nums.append(state)
            else:
                raise koji.GenericError("integer state should >=0 and <=5")
        elif isinstance(state, six.string_types):
            state_nums.append(koji.TASK_STATES[state])
        else:
            raise koji.GenericError("unacceptable state type")
    options = {}
    if state_nums:
        options['state'] = state_nums
    options['parent'] = None
    options['decode'] = True

    queryOpts = {}
    if limit:
        queryOpts['limit'] = limit
    if offset:
        queryOpts['offset'] = offset
    queryOpts['order'] = '-id'
    cvs = []
    with session.multicall() as m:
        if channel_ids:
            for channel_id in channel_ids:
                if methods:
                    for method in methods:
                        cvs.append((m.listTasks(
                            dict(options, method=method,
                                 channel_id=channel_id), queryOpts)))
                else:
                    cvs.append(
                        m.listTasks(dict(options, channel_id=channel_id),
                                    queryOpts))
        elif host_ids:
            for host_id in host_ids:
                if methods:
                    for method in methods:
                        cvs.append(
                            m.listTasks(
                                dict(options, method=method, host_id=host_id),
                                queryOpts))
                else:
                    cvs.append(
                        m.listTasks(dict(options, host_id=host_id), queryOpts))
        else:
            if methods:
                for method in methods:
                    cvs.append(
                        m.listTasks(dict(options, method=method), queryOpts))
            else:
                cvs.append(m.listTasks(options, queryOpts))
    tasks = sum([cv.result for cv in cvs], [])
    print(tasks)
    if not tasks:
        raise koji.GenericError("no tasks to replicate.")
    else:
        logger.debug("to replicate tasks:\n%s",
                     "\n".join([str(t['id']) for t in tasks]))
    return tasks
Esempio n. 15
0
 def do_mounts(self, rootdir, mounts):
     if not mounts:
         return
     self.logger.info('New runroot')
     self.logger.info("Runroot mounts: %s" % mounts)
     fn = '%s/tmp/runroot_mounts' % rootdir
     fslog = file(fn, 'a')
     logfile = "%s/do_mounts.log" % self.workdir
     uploadpath = self.getUploadDir()
     error = None
     for dev, path, type, opts in mounts:
         if not path.startswith('/'):
             raise koji.GenericError("invalid mount point: %s" % path)
         mpoint = "%s%s" % (rootdir, path)
         if opts is None:
             opts = []
         else:
             opts = opts.split(',')
         if 'bind' in opts:
             #make sure dir exists
             if not os.path.isdir(dev):
                 error = koji.GenericError(
                     "No such directory or mount: %s" % dev)
                 break
             type = 'none'
             if path is None:
                 #shorthand for "same path"
                 path = dev
         if 'bg' in opts:
             error = koji.GenericError(
                 "bad config: background mount not allowed")
             break
         opts = ','.join(opts)
         cmd = ['mount', '-t', type, '-o', opts, dev, mpoint]
         self.logger.info("Mount command: %r" % cmd)
         koji.ensuredir(mpoint)
         if compat_mode:
             status = log_output(cmd[0],
                                 cmd,
                                 logfile,
                                 uploadpath,
                                 logerror=True,
                                 append=True)
         else:
             status = log_output(self.session,
                                 cmd[0],
                                 cmd,
                                 logfile,
                                 uploadpath,
                                 logerror=True,
                                 append=True)
         if not _isSuccess(status):
             error = koji.GenericError("Unable to mount %s: %s" \
                     % (mpoint, _parseStatus(status, cmd)))
             break
         fslog.write("%s\n" % mpoint)
         fslog.flush()
     fslog.close()
     if error is not None:
         self.undo_mounts(rootdir, fatal=False)
         raise error
Esempio n. 16
0
 def test_bad_tag(self):
     self.get_tag.side_effect = koji.GenericError("FOO")
     with self.assertRaises(koji.GenericError):
         kojihub.delete_tag('badtag')
     self.assertEqual(self.updates, [])
     self.context.session.assertPerm.assert_called_with('admin')
Esempio n. 17
0
 def listTagged(self, tag, *args, **kwargs):
     raise koji.GenericError("foo")
Esempio n. 18
0
    def updateTasks(self):
        """Read and process task statuses from server

        The processing we do is:
            1) clean up after tasks that are not longer active:
                * kill off processes
                * retire buildroots
                * remove buildroots
                    - with some possible exceptions
            2) wake waiting tasks if appropriate
        """
        tasks = {}
        stale = []
        task_load = 0.0
        if self.pids:
            self.logger.info("pids: %r" % self.pids)
        for task in self.session.host.getHostTasks():
            self.logger.info("open task: %r" % task)
            # the tasks returned are those that are open and locked
            # by this host.
            id = task['id']
            if id not in self.pids:
                #We don't have a process for this
                #Expected to happen after a restart, otherwise this is an error
                stale.append(id)
                continue
            tasks[id] = task
            if task.get('alert', False):
                #wake up the process
                self.logger.info("Waking up task: %r" % task)
                os.kill(self.pids[id], signal.SIGUSR2)
            if not task['waiting']:
                task_load += task['weight']
        self.logger.debug("Task Load: %s" % task_load)
        self.task_load = task_load
        self.tasks = tasks
        self.logger.debug("Current tasks: %r" % self.tasks)
        if len(stale) > 0:
            #A stale task is one which is opened to us, but we know nothing
            #about). This will happen after a daemon restart, for example.
            self.logger.info("freeing stale tasks: %r" % stale)
            self.session.host.freeTasks(stale)
        for id, pid in self.pids.items():
            if self._waitTask(id, pid):
                # the subprocess handles most everything, we just need to clear things out
                if self.cleanupTask(id, wait=False):
                    del self.pids[id]
                if id in self.tasks:
                    del self.tasks[id]
        for id, pid in self.pids.items():
            if id not in tasks:
                # expected to happen when:
                #  - we are in the narrow gap between the time the task
                #    records its result and the time the process actually
                #    exits.
                #  - task is canceled
                #  - task is forcibly reassigned/unassigned
                tinfo = self.session.getTaskInfo(id)
                if tinfo is None:
                    raise koji.GenericError("Invalid task %r (pid %r)" % (id, pid))
                elif tinfo['state'] == koji.TASK_STATES['CANCELED']:
                    self.logger.info("Killing canceled task %r (pid %r)" % (id, pid))
                    if self.cleanupTask(id):
                        del self.pids[id]
                elif tinfo['host_id'] != self.host_id:
                    self.logger.info("Killing reassigned task %r (pid %r)" % (id, pid))
                    if self.cleanupTask(id):
                        del self.pids[id]
                else:
                    self.logger.info("Lingering task %r (pid %r)" % (id, pid))
Esempio n. 19
0
    def load_config(self, environ):
        """Load configuration options

        Options are read from a config file.

        Backwards compatibility:
            - if ConfigFile is not set, opts are loaded from http config
            - if ConfigFile is set, then the http config must not provide Koji options
            - In a future version we will load the default hub config regardless
            - all PythonOptions (except koji.web.ConfigFile) are now deprecated and
              support for them will disappear in a future version of Koji
        """
        modpy_opts = environ.get('modpy.opts', {})
        if 'modpy.opts' in environ:
            cf = modpy_opts.get('koji.web.ConfigFile', None)
            cfdir = modpy_opts.get('koji.web.ConfigDir', None)
            # to aid in the transition from PythonOptions to web.conf, we do
            # not check the config file by default, it must be configured
            if not cf and not cfdir:
                self.logger.warn(
                    'Warning: configuring Koji via PythonOptions is deprecated. Use web.conf'
                )
        else:
            cf = environ.get('koji.web.ConfigFile', '/etc/kojiweb/web.conf')
            cfdir = environ.get('koji.web.ConfigDir',
                                '/etc/kojiweb/web.conf.d')
        if cfdir:
            configs = koji.config_directory_contents(cfdir)
        else:
            configs = []
        if cf and os.path.isfile(cf):
            configs.append(cf)
        if configs:
            config = RawConfigParser()
            config.read(configs)
        elif modpy_opts:
            # presumably we are configured by modpy options
            config = None
        else:
            raise koji.GenericError("Configuration missing")

        opts = {}
        for name, dtype, default in self.cfgmap:
            if config:
                key = ('web', name)
                if config.has_option(*key):
                    if dtype == 'integer':
                        opts[name] = config.getint(*key)
                    elif dtype == 'boolean':
                        opts[name] = config.getboolean(*key)
                    elif dtype == 'list':
                        opts[name] = [
                            x.strip() for x in config.get(*key).split(',')
                        ]
                    else:
                        opts[name] = config.get(*key)
                else:
                    opts[name] = default
            else:
                if modpy_opts.get(name, None) is not None:
                    if dtype == 'integer':
                        opts[name] = int(modpy_opts.get(name))
                    elif dtype == 'boolean':
                        opts[name] = modpy_opts.get(name).lower() in ('yes',
                                                                      'on',
                                                                      'true',
                                                                      '1')
                    else:
                        opts[name] = modpy_opts.get(name)
                else:
                    opts[name] = default
        if 'modpy.conf' in environ:
            debug = environ['modpy.conf'].get('PythonDebug', '0').lower()
            opts['PythonDebug'] = (debug in ['yes', 'on', 'true', '1'])
        opts['Secret'] = koji.util.HiddenValue(opts['Secret'])
        self.options = opts
        return opts
Esempio n. 20
0
def _list_tasks(options, session):
    "Retrieve a list of tasks"

    callopts = {
        'decode': True,
    }
    if not getattr(options, 'all', False):
        callopts['state'] = [
            koji.TASK_STATES[s] for s in ('FREE', 'OPEN', 'ASSIGNED')
        ]

    if getattr(options, 'after', False):
        callopts['startedAfter'] = options.after
    if getattr(options, 'before', False):
        callopts['startedBefore'] = options.before

    if getattr(options, 'mine', False):
        if getattr(options, 'user', None):
            raise koji.GenericError(
                "Can't specify 'mine' and 'user' in same time")
        user = session.getLoggedInUser()
        if not user:
            print("Unable to determine user")
            sys.exit(1)
        callopts['owner'] = user['id']
    if getattr(options, 'user', None):
        user = session.getUser(options.user)
        if not user:
            print("No such user: %s" % options.user)
            sys.exit(1)
        callopts['owner'] = user['id']
    if getattr(options, 'arch', None):
        callopts['arch'] = parse_arches(options.arch, to_list=True)
    if getattr(options, 'method', None):
        callopts['method'] = options.method
    if getattr(options, 'channel', None):
        chan = session.getChannel(options.channel)
        if not chan:
            print("No such channel: %s" % options.channel)
            sys.exit(1)
        callopts['channel_id'] = chan['id']
    if getattr(options, 'host', None):
        host = session.getHost(options.host)
        if not host:
            print("No such host: %s" % options.host)
            sys.exit(1)
        callopts['host_id'] = host['id']

    qopts = {'order': 'priority,create_time'}
    tasklist = session.listTasks(callopts, qopts)
    tasks = dict([(x['id'], x) for x in tasklist])

    # thread the tasks
    for t in tasklist:
        if t['parent'] is not None:
            parent = tasks.get(t['parent'])
            if parent:
                parent.setdefault('children', [])
                parent['children'].append(t)
                t['sub'] = True

    return tasklist
 def test_koji_cg_koji_import(self, tag_loader, tagger, cl_session):
     """ Tests whether build is still tagged even if there's an exception in CGImport """
     cl_session.return_value.CGImport = Mock(
         side_effect=koji.GenericError("Build already exists asdv"))
     self.cg.koji_import()
     tagger.assert_called()
Esempio n. 22
0
def createSideTag(basetag, debuginfo=False, suffix=None):
    """Create a side tag.

    :param basetag: name or ID of base tag
    :type basetag: str or int

    :param debuginfo: should buildroot repos contain debuginfo?
    :type debuginfo: bool

    :param suffix: suffix which will be appended to generated sidetag name
                   List of allowed suffixes needs to be defined in config.
    :type suffix: str

    :returns dict: sidetag name + id
    """

    if suffix and suffix not in ALLOWED_SUFFIXES:
        raise koji.GenericError("%s suffix is not allowed for sidetag" % suffix)

    # Any logged-in user is able to request creation of side tags,
    # as long the request meets the policy.
    context.session.assertLogin()
    user = get_user(context.session.user_id, strict=True)

    basetag = get_tag(basetag, strict=True)

    query = QueryProcessor(
        tables=["tag_extra"],
        clauses=["key='sidetag_user_id'", "value=%(user_id)s", "active IS TRUE"],
        columns=["COUNT(*)"],
        aliases=["user_tags"],
        values={"user_id": str(user["id"])},
    )
    user_tags = query.executeOne()
    if user_tags is None:
        # should not ever happen
        raise koji.GenericError("Unknown db error")

    # Policy is a very flexible mechanism, that can restrict for which
    # tags sidetags can be created, or which users can create sidetags etc.
    assert_policy(
        "sidetag", {"tag": basetag["id"], "number_of_tags": user_tags["user_tags"]}
    )

    # ugly, it will waste one number in tag_id_seq, but result will match with
    # id assigned by _create_tag
    tag_id = nextval("tag_id_seq") + 1
    sidetag_name = "%s-side-%s" % (basetag["name"], tag_id)
    if suffix:
        sidetag_name += '-%s' % suffix
    extra = {
        "sidetag": True,
        "sidetag_user": user["name"],
        "sidetag_user_id": user["id"],
    }
    if debuginfo:
        extra['with_debuginfo'] = True
    sidetag_id = _create_tag(
        sidetag_name,
        parent=basetag["id"],
        arches=basetag["arches"],
        extra=extra,
    )
    _create_build_target(sidetag_name, sidetag_id, sidetag_id)

    return {"name": sidetag_name, "id": sidetag_id}
Esempio n. 23
0
 def get(self, name):
     func = self.funcs.get(name, None)
     if func is None:
         raise koji.GenericError("Invalid method: %s" % name)
     return func
Esempio n. 24
0
def getProductListings(productLabel, buildInfo):
    """
    Get a map of which variants of the given product included packages built
    by the given build, and which arches each variant included.
    """
    compose_dbh = Products.compose_get_dbh()

    #XXX - need access to hub kojihub functions
    conf = koji.read_config('brew')
    hub = conf['server']
    session = koji.ClientSession(hub, {})

    build = session.getBuild(buildInfo, strict=True)
    sys.stderr.write("%r" % build)
    sys.stderr.flush()
    rpms = session.listRPMs(buildID=build['id'])
    if not rpms:
        raise koji.GenericError("Could not find any RPMs for build: %s" %
                                buildInfo)

    # sort rpms, so first part of list consists of sorted 'normal' rpms and
    # second part are sorted debuginfos
    debuginfos = [x for x in rpms if '-debuginfo' in x['nvr']]
    base_rpms = [x for x in rpms if '-debuginfo' not in x['nvr']]
    rpms = sorted(base_rpms, key=lambda x: x['nvr']) + sorted(
        debuginfos, key=lambda x: x['nvr'])
    srpm = "%(package_name)s-%(version)s-%(release)s.src.rpm" % build

    prodinfo = Products.get_product_info(compose_dbh, productLabel)
    if not prodinfo:
        # no product with the given label exists
        raise koji.GenericError("Could not find a product with label: %s" %
                                productLabel)
    version, variants = prodinfo

    listings = {}
    match_version = Products.get_match_versions(compose_dbh, productLabel)
    for variant in variants:
        if variant == None:
            # dict keys must be a string
            variant = ''
        treelist = Products.precalc_treelist(compose_dbh, productLabel,
                                             version, variant)
        if not treelist:
            continue
        overrides = Products.get_overrides(compose_dbh, productLabel, version,
                                           variant)
        cache_map = {}
        for rpm in rpms:
            if rpm['name'] in match_version:
                rpm_version = rpm['version']
            else:
                rpm_version = None

        # without debuginfos
        rpms_nondebug = [
            rpm for rpm in rpms if not koji.is_debuginfo(rpm['name'])
        ]
        d = {}
        all_archs = set([rpm['arch'] for rpm in rpms_nondebug])
        for arch in all_archs:
            d[arch] = Products.dest_get_archs(
                compose_dbh,
                treelist,
                arch,
                [rpm['name'] for rpm in rpms_nondebug if rpm['arch'] == arch],
                cache_map.get(srpm, {}).get(arch, {}),
                rpm_version,
                overrides,
            )

        for rpm in rpms_nondebug:
            dest_archs = d[rpm['arch']].get(rpm['name'], {}).keys()
            if rpm['arch'] != 'src':
                cache_map.setdefault(srpm, {})
                cache_map[srpm].setdefault(rpm['arch'], {})
                for x in dest_archs:
                    cache_map[srpm][rpm['arch']][x] = 1
            for dest_arch in dest_archs:
                listings.setdefault(variant,
                                    {}).setdefault(rpm['nvr'], {}).setdefault(
                                        rpm['arch'], []).append(dest_arch)

        # debuginfo only
        rpms_debug = [rpm for rpm in rpms if koji.is_debuginfo(rpm['name'])]
        d = {}
        all_archs = set([rpm['arch'] for rpm in rpms_debug])
        for arch in all_archs:
            d[arch] = Products.dest_get_archs(
                compose_dbh,
                treelist,
                arch,
                [rpm['name'] for rpm in rpms_debug if rpm['arch'] == arch],
                cache_map.get(srpm, {}).get(arch, {}),
                rpm_version,
                overrides,
            )

        for rpm in rpms_debug:
            dest_archs = d[rpm['arch']].get(rpm['name'], {}).keys()
            if rpm['arch'] != 'src':
                cache_map.setdefault(srpm, {})
                cache_map[srpm].setdefault(rpm['arch'], {})
                for x in dest_archs:
                    cache_map[srpm][rpm['arch']][x] = 1
            for dest_arch in dest_archs:
                listings.setdefault(variant,
                                    {}).setdefault(rpm['nvr'], {}).setdefault(
                                        rpm['arch'], []).append(dest_arch)

        for variant in listings.keys():
            nvrs = listings[variant].keys()
            #BREW-260: Read allow_src_only flag for the product/version
            allow_src_only = Products.get_srconly_flag(compose_dbh,
                                                       productLabel, version)
            if len(nvrs) == 1:
                maps = listings[variant][nvrs[0]].keys()
                #BREW-260: check for allow_src_only flag added
                if len(maps) == 1 and maps[0] == 'src' and not allow_src_only:
                    del listings[variant]
    return listings
Esempio n. 25
0
    def test_handle_write_signed_rpm(self, activate_session_mock, stdout):
        """Test handle_write_signed_rpm function"""
        fake_sigkey = '64dab85d'
        arguments = [fake_sigkey]
        options = mock.MagicMock()
        session = mock.MagicMock()

        def get_expect_data(rpm_data):
            expected = ''
            calls = []
            for i, data in enumerate(rpm_data):
                nvra = "%(name)s-%(version)s-%(release)s.%(arch)s" % data
                expected += "[%d/%d] %s" % (i + 1, len(rpm_data), nvra) + "\n"
                calls.append(call(data['id'], fake_sigkey))
            return expected, calls

        # Case 1, specifies N-V-R-A or N-V-R format RPM
        # result: write sigkey to specified RPMs
        rpm_data = [GET_RPM_RESULTS[0], GET_RPM_RESULTS[3]]

        session.getRPM.side_effect = [
            rpm_data[0],  # bash-4.4.12-5.fc26.src
            koji.GenericError  # bash-4.4.12-5.fc26
        ]
        session.getBuild.return_value = {
            'package_name': 'bash',
            'id': 1,
            'version': '4.4.12',
            'nvr': 'bash-4.4.12-5.fc26',
            'name': 'bash',
            'release': '5.fc26'
        }
        session.listRPMs.return_value = [rpm_data[1]]  # bash-4.4.12-5.fc26
        args = arguments + ['bash-4.4.12-5.fc26.src', 'bash-4.4.12-5.fc26']
        expect_msg, expect_calls = get_expect_data(rpm_data)

        handle_write_signed_rpm(options, session, args)
        self.assert_console_message(stdout, expect_msg)
        session.writeSignedRPM.assert_has_calls(expect_calls)
        session.queryRPMSigs.assert_not_called()

        # Case 2, with --all option
        # result: write sigkey to all RPMS
        session.queryRPMSigs.return_value = QUERY_RPM_RESULTS
        session.getRPM.side_effect = GET_RPM_RESULTS
        expect_msg, expect_calls = get_expect_data(GET_RPM_RESULTS)

        handle_write_signed_rpm(options, session, arguments + ['--all'])
        self.assert_console_message(stdout, expect_msg)
        session.writeSignedRPM.assert_has_calls(expect_calls)
        session.queryRPMSigs.assert_called_with(sigkey=fake_sigkey)

        session.queryRPMSigs.reset_mock()

        # Case 3, with --buildid option
        # result: write sigkey to specified build id RPM
        rpm_data = [
            GET_RPM_RESULTS[0],  # build_id = 1
            GET_RPM_RESULTS[3]
        ]  # build_id = 1
        session.listRPMs.return_value = rpm_data
        expect_msg, expect_calls = get_expect_data(rpm_data)

        handle_write_signed_rpm(options, session,
                                arguments + ['--buildid', '1'])
        self.assert_console_message(stdout, expect_msg)
        session.listRPMs.assert_called_with(1)
        session.queryRPMSigs.assert_not_called()
        session.writeSignedRPM.assert_has_calls(expect_calls)

        session.listRPM.reset_mock()
        session.writeSignedRPM.reset_mock()

        # Case 4, RPM not exist
        # result: raise koji.GenericError
        session.getRPM.side_effect = koji.GenericError('fake-get-rpm-error')
        session.getBuild.return_value = None

        args = arguments + ['gawk-4.1.4-3.fc26.x86_64']
        with self.assertRaises(koji.GenericError) as cm:
            handle_write_signed_rpm(options, session, args)
        self.assertEqual(str(cm.exception),
                         'No such rpm or build: %s' % args[1])

        session.listRPM.assert_not_called()
        session.queryRPMSigs.assert_not_called()
        session.writeSignedRPM.assert_not_called()
Esempio n. 26
0
    def getTag(self, taginfo, **kw):
        """
        Retrieve the given tag from koji.

        Args:
            taginfo (int or str): The tag you want info about.
            strict (bool): If True, raise an Exception if epel tags are queried. Defaults to False.
        Returns:
            dict or None: A dictionary of tag information, or None if epel is requested and strict
                is False.
        Raises:
            koji.GenericError: If strict is True and epel is requested.
        """
        if isinstance(taginfo, int):
            taginfo = "f%d" % taginfo

        if taginfo.startswith("epel"):
            if kw.get("strict", False):
                raise koji.GenericError("Invalid tagInfo: '%s'" % taginfo)

            else:
                return None

        # These tags needs to be created
        if taginfo in [
                "f32-build-side-1234-signing-pending",
                "f32-build-side-1234-testing-pending"
        ]:
            return None

        # emulate a side-tag response
        if taginfo in self._side_tag_ids_names:
            for sidetag in self.__side_tags__:
                if taginfo in (sidetag['id'], sidetag['name']):
                    return {
                        'maven_support': False,
                        'locked': False,
                        'name': sidetag['name'],
                        'extra': {
                            'sidetag_user': '******',
                            'sidetag': True
                        },
                        'perm': None,
                        'perm_id': None,
                        'arches': None,
                        'maven_include_all': False,
                        'id': sidetag['id']
                    }

            if kw.get('strict'):
                raise koji.GenericError("Invalid tagInfo: '%s'" % taginfo)
            else:
                return None

        return {
            'maven_support': False,
            'locked': False,
            'name': taginfo,
            'extra': {},
            'perm': None,
            'id': 246,
            'arches': None,
            'maven_include_all': False,
            'perm_id': None
        }
Esempio n. 27
0
 def listTagged(self, tag, *args, **kwargs):
     raise koji.GenericError(f"Invalid tagInfo: {tag!r}")
Esempio n. 28
0
    def wait(self,
             subtasks=None,
             all=False,
             failany=False,
             canfail=None,
             timeout=None):
        """Wait on subtasks

        subtasks is a list of integers (or an integer). If more than one subtask
        is specified, then the default behavior is to return when any of those
        tasks complete. However, if all is set to True, then it waits for all of
        them to complete.

        If all and failany are both set to True, then each finished task will
        be checked for failure, and a failure will cause all of the unfinished
        tasks to be cancelled.

        If canfail is given a list of task ids, then those tasks can fail
        without affecting the other tasks.

        If timeout is specified, then subtasks will be failed and an exception
        raised when the timeout is exceeded.

        special values:
            subtasks = None     specify all subtasks

        Implementation notes:
            The build daemon forks all tasks as separate processes. This function
            uses signal.pause to sleep. The main process watches subtasks in
            the database and will send the subprocess corresponding to the
            subtask a SIGUSR2 to wake it up when subtasks complete.
        """

        if canfail is None:
            checked = set()
        else:
            # canfail task are marked as checked
            checked = set(canfail)
        if isinstance(subtasks, int):
            # allow single integer w/o enclosing list
            subtasks = [subtasks]
        self.session.host.taskSetWait(self.id, subtasks)
        self.logger.debug("Waiting on %r" % subtasks)
        start = time.time()
        while True:
            finished, unfinished = self.session.host.taskWait(self.id)
            if len(unfinished) == 0:
                #all done
                break
            elif len(finished) > 0:
                if all:
                    if failany:
                        # we care only about tasks which are not correctly
                        # finished and in same time not in canfail list
                        for task in set(finished) - checked:
                            try:
                                self.session.getTaskResult(task)
                                checked.add(task)
                            except (koji.GenericError,
                                    six.moves.xmlrpc_client.Fault):
                                self.logger.info(
                                    "task %s failed or was canceled, cancelling unfinished tasks"
                                    % task)
                                self.session.cancelTaskChildren(self.id)
                                # reraise the original error now, rather than waiting for
                                # an error in taskWaitResults()
                                raise
                else:
                    # at least one done
                    break
            if timeout:
                # sleep until timeout is up (or let main process wake us up)
                remain = start + timeout - time.time()
                if remain > 0:
                    self.logger.debug("Sleeping for %.1fs", remain)
                    time.sleep(remain)
                # check if we're timed out
                duration = time.time() - start
                if duration > timeout:
                    self.logger.info('Subtasks timed out')
                    self.session.cancelTaskChildren(self.id)
                    raise koji.GenericError('Subtasks timed out after %.1f '
                                            'seconds' % duration)
            else:
                # signal handler set by TaskManager.forkTask
                self.logger.debug("Pausing...")
                signal.pause()
                # main process will wake us up with SIGUSR2
                self.logger.debug("...waking up")

        self.logger.debug("Finished waiting")
        if all:
            finished = subtasks
        return dict(
            self.session.host.taskWaitResults(self.id,
                                              finished,
                                              canfail=canfail))
Esempio n. 29
0
 def mock_getTaskResult(task_id):
     if task_id == 4:
         raise koji.GenericError()
 def mock_get_rpm(rpmID, strict=False):
     if strict:
         raise koji.GenericError('msg')
     else:
         return None