PR#4396: channel defaults
Merges #4396 https://pagure.io/koji/pull-request/4396 Fixes: #4399 https://pagure.io/koji/issue/4399 channel policy cannot distinguish override from default
This commit is contained in:
commit
58440960f7
4 changed files with 173 additions and 27 deletions
|
|
@ -605,7 +605,8 @@ def make_task(method, arglist, **opts):
|
|||
parent: the id of the parent task (creates a subtask)
|
||||
label: (subtasks only) the label of the subtask
|
||||
owner: the user_id that should own the task
|
||||
channel: the channel to place the task in
|
||||
channel: requested channel override
|
||||
default_channel: default channel
|
||||
arch: the arch for the task
|
||||
priority: the priority of the task
|
||||
assign: a host_id to assign the task to
|
||||
|
|
@ -659,6 +660,12 @@ def make_task(method, arglist, **opts):
|
|||
for key in 'arch', 'parent', 'label', 'owner':
|
||||
policy_data[key] = opts[key]
|
||||
policy_data['user_id'] = opts['owner']
|
||||
default_channel = 'default'
|
||||
if 'default_channel' in opts:
|
||||
default_channel = opts['default_channel']
|
||||
if 'channel' not in opts and context.opts.get('DefaultChannelCompat'):
|
||||
# in the compat case, treat explicit default as an override
|
||||
opts['channel'] = default_channel
|
||||
if 'channel' in opts:
|
||||
policy_data['req_channel'] = opts['channel']
|
||||
channel_info = get_channel(opts['channel'])
|
||||
|
|
@ -673,8 +680,8 @@ def make_task(method, arglist, **opts):
|
|||
ruleset = context.policy.get('channel')
|
||||
result = ruleset.apply(policy_data)
|
||||
if result is None:
|
||||
logger.warning('Channel policy returned no result, using default')
|
||||
opts['channel_id'] = get_channel_id('default', strict=True)
|
||||
logger.debug('Channel policy returned no result, using default')
|
||||
opts['channel_id'] = get_channel_id(default_channel, strict=True)
|
||||
else:
|
||||
try:
|
||||
parts = result.split()
|
||||
|
|
@ -696,6 +703,9 @@ def make_task(method, arglist, **opts):
|
|||
ruleset.last_rule())
|
||||
raise koji.GenericError("invalid channel policy")
|
||||
opts['channel_id'] = req_channel_id
|
||||
elif parts[0] == "default":
|
||||
# note this is different from "use default" if default_channel is passed
|
||||
opts['channel_id'] = get_channel_id(default_channel, strict=True)
|
||||
else:
|
||||
logger.error("Invalid result from channel policy: %s", ruleset.last_rule())
|
||||
raise koji.GenericError("invalid channel policy")
|
||||
|
|
@ -10904,7 +10914,7 @@ class RootExports(object):
|
|||
|
||||
return make_task('chainbuild', [srcs, target, opts], **taskOpts)
|
||||
|
||||
def mavenBuild(self, url, target, opts=None, priority=None, channel='maven'):
|
||||
def mavenBuild(self, url, target, opts=None, priority=None, channel=None):
|
||||
"""Create a Maven build task
|
||||
|
||||
url: The url to checkout the source from. May be a CVS, SVN, or GIT repository.
|
||||
|
|
@ -10912,7 +10922,7 @@ class RootExports(object):
|
|||
priority: the amount to increase (or decrease) the task priority, relative
|
||||
to the default priority; higher values mean lower priority; only
|
||||
admins have the right to specify a negative priority here
|
||||
channel: the channel to allocate the task to (defaults to the "maven" channel)
|
||||
channel: override the channel to allocate the task to
|
||||
|
||||
Returns the task ID
|
||||
"""
|
||||
|
|
@ -10922,7 +10932,7 @@ class RootExports(object):
|
|||
convert_value(url, cast=str, check_only=True)
|
||||
if not opts:
|
||||
opts = {}
|
||||
taskOpts = {}
|
||||
taskOpts = {'default_channel': 'maven'}
|
||||
if priority:
|
||||
if priority < 0:
|
||||
if not context.session.hasPerm('admin'):
|
||||
|
|
@ -10933,7 +10943,7 @@ class RootExports(object):
|
|||
|
||||
return make_task('maven', [url, target, opts], **taskOpts)
|
||||
|
||||
def wrapperRPM(self, build, url, target, priority=None, channel='maven', opts=None):
|
||||
def wrapperRPM(self, build, url, target, priority=None, channel=None, opts=None):
|
||||
"""Create a top-level wrapperRPM task
|
||||
|
||||
build: The build to generate wrapper rpms for. Must be in the COMPLETE state and have no
|
||||
|
|
@ -10945,7 +10955,7 @@ class RootExports(object):
|
|||
priority: the amount to increase (or decrease) the task priority, relative
|
||||
to the default priority; higher values mean lower priority; only
|
||||
admins have the right to specify a negative priority here
|
||||
channel: the channel to allocate the task to (defaults to the "maven" channel)
|
||||
channel: override the channel to allocate the task to
|
||||
|
||||
returns the task ID
|
||||
"""
|
||||
|
|
@ -10968,18 +10978,19 @@ class RootExports(object):
|
|||
logger.warning('The wrapperRPM call ignores repo_id options')
|
||||
del opts['repo_id']
|
||||
|
||||
taskOpts = {}
|
||||
taskOpts = {'default_channel': 'maven'}
|
||||
if priority:
|
||||
if priority < 0:
|
||||
if not context.session.hasPerm('admin'):
|
||||
raise koji.ActionNotAllowed('only admins may create high-priority tasks')
|
||||
taskOpts['priority'] = koji.PRIO_DEFAULT + priority
|
||||
convert_value(channel, cast=str, check_only=True)
|
||||
taskOpts['channel'] = channel
|
||||
if channel is not None:
|
||||
convert_value(channel, cast=str, check_only=True)
|
||||
taskOpts['channel'] = channel
|
||||
|
||||
return make_task('wrapperRPM', [url, build_target, build, None, opts], **taskOpts)
|
||||
|
||||
def chainMaven(self, builds, target, opts=None, priority=None, channel='maven'):
|
||||
def chainMaven(self, builds, target, opts=None, priority=None, channel=None):
|
||||
"""Create a Maven chain-build task
|
||||
|
||||
builds: a list of maps defining the parameters for the sequence of builds
|
||||
|
|
@ -10987,7 +10998,7 @@ class RootExports(object):
|
|||
priority: the amount to increase (or decrease) the task priority, relative
|
||||
to the default priority; higher values mean lower priority; only
|
||||
admins have the right to specify a negative priority here
|
||||
channel: the channel to allocate the task to (defaults to the "maven" channel)
|
||||
channel: override the channel to allocate the task to
|
||||
|
||||
Returns the task ID
|
||||
"""
|
||||
|
|
@ -10995,7 +11006,7 @@ class RootExports(object):
|
|||
if not context.opts.get('EnableMaven'):
|
||||
raise koji.GenericError("Maven support not enabled")
|
||||
convert_value(builds, cast=dict, check_only=True)
|
||||
taskOpts = {}
|
||||
taskOpts = {'default_channel': 'maven'}
|
||||
if priority:
|
||||
if priority < 0:
|
||||
if not context.session.hasPerm('admin'):
|
||||
|
|
@ -11006,7 +11017,7 @@ class RootExports(object):
|
|||
|
||||
return make_task('chainmaven', [builds, target, opts], **taskOpts)
|
||||
|
||||
def winBuild(self, vm, url, target, opts=None, priority=None, channel='vm'):
|
||||
def winBuild(self, vm, url, target, opts=None, priority=None, channel=None):
|
||||
"""
|
||||
Create a Windows build task
|
||||
|
||||
|
|
@ -11017,7 +11028,7 @@ class RootExports(object):
|
|||
priority: the amount to increase (or decrease) the task priority, relative
|
||||
to the default priority; higher values mean lower priority; only
|
||||
admins have the right to specify a negative priority here
|
||||
channel: the channel to allocate the task to (defaults to the "vm" channel)
|
||||
channel: override the channel to allocate the task to
|
||||
|
||||
Returns the task ID
|
||||
"""
|
||||
|
|
@ -11032,7 +11043,7 @@ class RootExports(object):
|
|||
assert_policy('vm', policy_data)
|
||||
if not opts:
|
||||
opts = {}
|
||||
taskOpts = {}
|
||||
taskOpts = {'default_channel': 'vm'}
|
||||
if priority:
|
||||
if priority < 0:
|
||||
if not context.session.hasPerm('admin'):
|
||||
|
|
@ -11057,7 +11068,7 @@ class RootExports(object):
|
|||
|
||||
context.session.assertPerm(img_type)
|
||||
|
||||
taskOpts = {'channel': img_type}
|
||||
taskOpts = {'default_channel': img_type}
|
||||
if img_type == 'livemedia':
|
||||
taskOpts['arch'] = 'noarch'
|
||||
else:
|
||||
|
|
@ -11079,7 +11090,7 @@ class RootExports(object):
|
|||
Create an image using two other images and an indirection template
|
||||
"""
|
||||
context.session.assertPerm('image')
|
||||
taskOpts = {'channel': 'image'}
|
||||
taskOpts = {'default_channel': 'image'}
|
||||
if priority:
|
||||
if priority < 0:
|
||||
if not context.session.hasPerm('admin'):
|
||||
|
|
@ -11104,7 +11115,7 @@ class RootExports(object):
|
|||
for i in [name, inst_tree, version]:
|
||||
convert_value(i, cast=str, check_only=True)
|
||||
context.session.assertPerm('image')
|
||||
taskOpts = {'channel': 'image'}
|
||||
taskOpts = {'default_channel': 'image'}
|
||||
if priority:
|
||||
if priority < 0:
|
||||
if not context.session.hasPerm('admin'):
|
||||
|
|
@ -13742,7 +13753,7 @@ class RootExports(object):
|
|||
logger.debug("Cancelling distRepo task %d" % task_id)
|
||||
Task(task_id).cancel(recurse=True)
|
||||
return make_task('distRepo', [tag, repo_id, keys, task_opts],
|
||||
priority=15, channel='createrepo')
|
||||
priority=15, default_channel='createrepo')
|
||||
|
||||
def newRepo(self, tag, event=None, src=False, debuginfo=False, separate_src=False):
|
||||
"""Create a newRepo task. returns task id"""
|
||||
|
|
@ -13770,7 +13781,7 @@ class RootExports(object):
|
|||
if debuginfo:
|
||||
opts['debuginfo'] = True
|
||||
args = koji.encode_args(tag, **opts)
|
||||
return make_task('newRepo', args, priority=15, channel='createrepo')
|
||||
return make_task('newRepo', args, priority=15, default_channel='createrepo')
|
||||
|
||||
def repoExpire(self, repo_id):
|
||||
"""mark repo expired"""
|
||||
|
|
|
|||
|
|
@ -472,6 +472,8 @@ config_map = [
|
|||
['EnableMaven', 'boolean', False],
|
||||
['EnableWin', 'boolean', False],
|
||||
|
||||
['DefaultChannelCompat', 'boolean', True],
|
||||
|
||||
['RLIMIT_AS', 'string', None],
|
||||
['RLIMIT_CORE', 'string', None],
|
||||
['RLIMIT_CPU', 'string', None],
|
||||
|
|
@ -630,7 +632,7 @@ _default_policies = {
|
|||
'channel': '''
|
||||
has req_channel :: req
|
||||
is_child_task :: parent
|
||||
all :: use default
|
||||
all :: default
|
||||
''',
|
||||
'vm': '''
|
||||
has_perm admin win-admin :: allow
|
||||
|
|
|
|||
|
|
@ -5719,7 +5719,7 @@
|
|||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"default": "'maven'"
|
||||
"default": "None"
|
||||
}
|
||||
],
|
||||
"varargs": null,
|
||||
|
|
@ -9363,7 +9363,7 @@
|
|||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"default": "'maven'"
|
||||
"default": "None"
|
||||
}
|
||||
],
|
||||
"varargs": null,
|
||||
|
|
@ -10312,7 +10312,7 @@
|
|||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"default": "'vm'"
|
||||
"default": "None"
|
||||
}
|
||||
],
|
||||
"varargs": null,
|
||||
|
|
@ -10342,7 +10342,7 @@
|
|||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"default": "'maven'"
|
||||
"default": "None"
|
||||
},
|
||||
{
|
||||
"name": "opts",
|
||||
|
|
|
|||
133
tests/test_hub/test_make_task.py
Normal file
133
tests/test_hub/test_make_task.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import unittest
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from kojihub import kojihub, kojixmlrpc
|
||||
|
||||
QP = kojihub.QueryProcessor
|
||||
IP = kojihub.InsertProcessor
|
||||
|
||||
|
||||
class TestMakeTask(unittest.TestCase):
|
||||
def getQuery(self, *args, **kwargs):
|
||||
query = QP(*args, **kwargs)
|
||||
query.execute = mock.MagicMock()
|
||||
query.executeOne = mock.MagicMock()
|
||||
self.queries.append(query)
|
||||
return query
|
||||
|
||||
def getInsert(self, *args, **kwargs):
|
||||
insert = IP(*args, **kwargs)
|
||||
insert.execute = mock.MagicMock()
|
||||
self.inserts.append(insert)
|
||||
return insert
|
||||
|
||||
def setUp(self):
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# self.get_user = mock.patch('kojihub.kojihub.get_user').start()
|
||||
self.opts = {
|
||||
'DefaultChannelCompat': True,
|
||||
'policy': {
|
||||
'channel': kojixmlrpc._default_policies['channel'],
|
||||
'priority': kojixmlrpc._default_policies['priority'],
|
||||
}
|
||||
}
|
||||
self.context.opts = self.opts
|
||||
|
||||
self._dml = mock.patch('kojihub.db._dml').start()
|
||||
self.QueryProcessor = mock.patch('kojihub.kojihub.QueryProcessor',
|
||||
side_effect=self.getQuery).start()
|
||||
self.queries = []
|
||||
self.InsertProcessor = mock.patch('kojihub.kojihub.InsertProcessor',
|
||||
side_effect=self.getInsert).start()
|
||||
self.inserts = []
|
||||
|
||||
self.get_channel = mock.patch('kojihub.kojihub.get_channel').start()
|
||||
self.get_channel_id = mock.patch('kojihub.kojihub.get_channel_id').start()
|
||||
self.currval = mock.patch('kojihub.kojihub.currval').start()
|
||||
self.auto_arch_refuse = mock.patch('kojihub.scheduler.auto_arch_refuse').start()
|
||||
|
||||
self.set_policy()
|
||||
|
||||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
|
||||
def set_policy(self, opts=None):
|
||||
# set policy from options
|
||||
if opts is None:
|
||||
opts = self.context.opts
|
||||
plugins = {}
|
||||
policy = kojixmlrpc.get_policy(opts, plugins)
|
||||
self.context.policy = policy or {}
|
||||
|
||||
def test_make_task_simple(self):
|
||||
self.get_channel_id.return_value = 1
|
||||
|
||||
kojihub.make_task('something', [1, 2, 3])
|
||||
|
||||
self.get_channel_id.assert_called_once_with('default', strict=True)
|
||||
self.assertEqual(len(self.inserts), 1)
|
||||
expected = {'state': 0, 'method': 'something', 'parent': None, 'arch': 'noarch',
|
||||
'channel_id': 1}
|
||||
for key in expected:
|
||||
self.assertEqual(self.inserts[0].data[key], expected[key])
|
||||
|
||||
def test_make_task_default_channel_compat(self):
|
||||
self.get_channel.return_value = {'name': 'testing', 'id': 23, 'enabled': True}
|
||||
self.opts['DefaultChannelCompat'] = True
|
||||
self.opts['policy']['channel'] = '''
|
||||
has req_channel :: req
|
||||
all :: use bad
|
||||
'''
|
||||
self.set_policy()
|
||||
|
||||
kojihub.make_task('something', [1, 2, 3], default_channel='testing')
|
||||
|
||||
# in compat mode we expect to hit the "req" policy result
|
||||
self.get_channel.assert_called_once_with('testing')
|
||||
self.get_channel_id.assert_not_called()
|
||||
self.assertEqual(len(self.inserts), 1)
|
||||
expected = {'state': 0, 'method': 'something', 'parent': None, 'arch': 'noarch',
|
||||
'channel_id': 23}
|
||||
for key in expected:
|
||||
self.assertEqual(self.inserts[0].data[key], expected[key])
|
||||
|
||||
def test_make_task_default_channel_nocompat(self):
|
||||
self.get_channel_id.return_value = 23
|
||||
self.opts['DefaultChannelCompat'] = False
|
||||
self.opts['policy']['channel'] = '''
|
||||
has req_channel :: use bad
|
||||
all :: default
|
||||
'''
|
||||
self.set_policy()
|
||||
|
||||
kojihub.make_task('something', [1, 2, 3], default_channel='testing')
|
||||
|
||||
# without compat mode we expect to hit the "default" policy result
|
||||
self.get_channel_id.assert_called_once_with('testing', strict=True)
|
||||
self.get_channel.assert_not_called()
|
||||
self.assertEqual(len(self.inserts), 1)
|
||||
expected = {'state': 0, 'method': 'something', 'parent': None, 'arch': 'noarch',
|
||||
'channel_id': 23}
|
||||
for key in expected:
|
||||
self.assertEqual(self.inserts[0].data[key], expected[key])
|
||||
|
||||
def test_make_task_no_policy_result(self):
|
||||
# if channel policy does not match, we should use the default
|
||||
self.get_channel.return_value = {'name': 'testing', 'id': 23, 'enabled': True}
|
||||
self.get_channel_id.return_value = 23
|
||||
self.opts['policy']['channel'] = ''
|
||||
self.set_policy()
|
||||
|
||||
kojihub.make_task('something', [1, 2, 3], default_channel='testing')
|
||||
|
||||
self.get_channel_id.assert_called_once_with('testing', strict=True)
|
||||
self.get_channel.assert_called_once_with('testing')
|
||||
self.assertEqual(len(self.inserts), 1)
|
||||
expected = {'state': 0, 'method': 'something', 'parent': None, 'arch': 'noarch',
|
||||
'channel_id': 23}
|
||||
for key in expected:
|
||||
self.assertEqual(self.inserts[0].data[key], expected[key])
|
||||
|
||||
# the end
|
||||
Loading…
Add table
Add a link
Reference in a new issue