draft builds
This commit is contained in:
parent
124450cec0
commit
87409499a3
30 changed files with 1763 additions and 186 deletions
|
|
@ -1102,6 +1102,8 @@ class BuildTask(BaseTaskHandler):
|
||||||
% (data['name'], target_info['dest_tag_name']))
|
% (data['name'], target_info['dest_tag_name']))
|
||||||
# TODO - more pre tests
|
# TODO - more pre tests
|
||||||
archlist = self.getArchList(build_tag, h, extra=extra_arches)
|
archlist = self.getArchList(build_tag, h, extra=extra_arches)
|
||||||
|
# pass draft option in
|
||||||
|
data['draft'] = opts.get('draft')
|
||||||
# let the system know about the build we're attempting
|
# let the system know about the build we're attempting
|
||||||
if not self.opts.get('scratch'):
|
if not self.opts.get('scratch'):
|
||||||
# scratch builds do not get imported
|
# scratch builds do not get imported
|
||||||
|
|
@ -2176,6 +2178,8 @@ class WrapperRPMTask(BaseBuildTask):
|
||||||
data['extra'] = {'source': {'original_url': source['url']}}
|
data['extra'] = {'source': {'original_url': source['url']}}
|
||||||
if opts.get('custom_user_metadata'):
|
if opts.get('custom_user_metadata'):
|
||||||
data['extra']['custom_user_metadata'] = opts['custom_user_metadata']
|
data['extra']['custom_user_metadata'] = opts['custom_user_metadata']
|
||||||
|
# pass draft option in
|
||||||
|
data['draft'] = opts.get('draft')
|
||||||
self.logger.info("Reading package config for %(name)s" % data)
|
self.logger.info("Reading package config for %(name)s" % data)
|
||||||
pkg_cfg = self.session.getPackageConfig(build_target['dest_tag'], data['name'])
|
pkg_cfg = self.session.getPackageConfig(build_target['dest_tag'], data['name'])
|
||||||
if not opts.get('skip_tag'):
|
if not opts.get('skip_tag'):
|
||||||
|
|
@ -5249,6 +5253,7 @@ Subject: %(nvr)s %(result)s %(operation)s by %(user_name)s\r
|
||||||
To: %(to_addrs)s\r
|
To: %(to_addrs)s\r
|
||||||
X-Koji-Package: %(pkg_name)s\r
|
X-Koji-Package: %(pkg_name)s\r
|
||||||
X-Koji-NVR: %(nvr)s\r
|
X-Koji-NVR: %(nvr)s\r
|
||||||
|
X-Koji-Draft: %(draft)s\r
|
||||||
X-Koji-User: %(user_name)s\r
|
X-Koji-User: %(user_name)s\r
|
||||||
X-Koji-Status: %(status)s\r
|
X-Koji-Status: %(status)s\r
|
||||||
%(tag_headers)s\r
|
%(tag_headers)s\r
|
||||||
|
|
@ -5278,6 +5283,7 @@ Status: %(status)s\r
|
||||||
user = self.session.getUser(user_info)
|
user = self.session.getUser(user_info)
|
||||||
pkg_name = build['package_name']
|
pkg_name = build['package_name']
|
||||||
nvr = koji.buildLabel(build)
|
nvr = koji.buildLabel(build)
|
||||||
|
draft = build.get('draft', False)
|
||||||
user_name = user['name']
|
user_name = user['name']
|
||||||
|
|
||||||
from_addr = self.options.from_addr
|
from_addr = self.options.from_addr
|
||||||
|
|
@ -5349,6 +5355,7 @@ X-Koji-Tag: %(dest_tag)s\r
|
||||||
X-Koji-Package: %(build_pkg_name)s\r
|
X-Koji-Package: %(build_pkg_name)s\r
|
||||||
X-Koji-Builder: %(build_owner)s\r
|
X-Koji-Builder: %(build_owner)s\r
|
||||||
X-Koji-Status: %(status)s\r
|
X-Koji-Status: %(status)s\r
|
||||||
|
X-Koji-Draft: %(draft)s\r
|
||||||
\r
|
\r
|
||||||
Package: %(build_nvr)s\r
|
Package: %(build_nvr)s\r
|
||||||
Tag: %(dest_tag)s\r
|
Tag: %(dest_tag)s\r
|
||||||
|
|
@ -5448,6 +5455,7 @@ Build Info: %(weburl)s/buildinfo?buildID=%(build_id)i\r
|
||||||
build_nvr = koji.buildLabel(build)
|
build_nvr = koji.buildLabel(build)
|
||||||
build_id = build['id']
|
build_id = build['id']
|
||||||
build_owner = build['owner_name']
|
build_owner = build['owner_name']
|
||||||
|
draft = build.get('draft', False)
|
||||||
# target comes from session.py:_get_build_target()
|
# target comes from session.py:_get_build_target()
|
||||||
dest_tag = None
|
dest_tag = None
|
||||||
if target is not None:
|
if target is not None:
|
||||||
|
|
|
||||||
|
|
@ -580,6 +580,8 @@ def handle_build(options, session, args):
|
||||||
parser.add_option("--custom-user-metadata", type="str",
|
parser.add_option("--custom-user-metadata", type="str",
|
||||||
help="Provide a JSON string of custom metadata to be deserialized and "
|
help="Provide a JSON string of custom metadata to be deserialized and "
|
||||||
"stored under the build's extra.custom_user_metadata field")
|
"stored under the build's extra.custom_user_metadata field")
|
||||||
|
parser.add_option("--draft", action="store_true",
|
||||||
|
help="Build draft build instead")
|
||||||
(build_opts, args) = parser.parse_args(args)
|
(build_opts, args) = parser.parse_args(args)
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
parser.error("Exactly two arguments (a build target and a SCM URL or srpm file) are "
|
parser.error("Exactly two arguments (a build target and a SCM URL or srpm file) are "
|
||||||
|
|
@ -588,6 +590,8 @@ def handle_build(options, session, args):
|
||||||
parser.error("--no-/rebuild-srpm is only allowed for --scratch builds")
|
parser.error("--no-/rebuild-srpm is only allowed for --scratch builds")
|
||||||
if build_opts.arch_override and not build_opts.scratch:
|
if build_opts.arch_override and not build_opts.scratch:
|
||||||
parser.error("--arch_override is only allowed for --scratch builds")
|
parser.error("--arch_override is only allowed for --scratch builds")
|
||||||
|
if build_opts.scratch and build_opts.draft:
|
||||||
|
parser.error("--scratch and --draft cannot be both specfied")
|
||||||
custom_user_metadata = {}
|
custom_user_metadata = {}
|
||||||
if build_opts.custom_user_metadata:
|
if build_opts.custom_user_metadata:
|
||||||
try:
|
try:
|
||||||
|
|
@ -618,7 +622,7 @@ def handle_build(options, session, args):
|
||||||
if build_opts.arch_override:
|
if build_opts.arch_override:
|
||||||
opts['arch_override'] = koji.parse_arches(build_opts.arch_override)
|
opts['arch_override'] = koji.parse_arches(build_opts.arch_override)
|
||||||
for key in ('skip_tag', 'scratch', 'repo_id', 'fail_fast', 'wait_repo', 'wait_builds',
|
for key in ('skip_tag', 'scratch', 'repo_id', 'fail_fast', 'wait_repo', 'wait_builds',
|
||||||
'rebuild_srpm'):
|
'rebuild_srpm', 'draft'):
|
||||||
val = getattr(build_opts, key)
|
val = getattr(build_opts, key)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
opts[key] = val
|
opts[key] = val
|
||||||
|
|
@ -830,6 +834,8 @@ def handle_wrapper_rpm(options, session, args):
|
||||||
parser.add_option("--nowait", action="store_false", dest="wait", help="Don't wait on build")
|
parser.add_option("--nowait", action="store_false", dest="wait", help="Don't wait on build")
|
||||||
parser.add_option("--background", action="store_true",
|
parser.add_option("--background", action="store_true",
|
||||||
help="Run the build at a lower priority")
|
help="Run the build at a lower priority")
|
||||||
|
parser.add_option("--draft", action="store_true",
|
||||||
|
help="Build draft build instead")
|
||||||
|
|
||||||
(build_opts, args) = parser.parse_args(args)
|
(build_opts, args) = parser.parse_args(args)
|
||||||
if build_opts.inis:
|
if build_opts.inis:
|
||||||
|
|
@ -839,6 +845,8 @@ def handle_wrapper_rpm(options, session, args):
|
||||||
if len(args) < 3:
|
if len(args) < 3:
|
||||||
parser.error("You must provide a build target, a build ID or NVR, "
|
parser.error("You must provide a build target, a build ID or NVR, "
|
||||||
"and a SCM URL to a specfile fragment")
|
"and a SCM URL to a specfile fragment")
|
||||||
|
if build_opts.scratch and build_opts.draft:
|
||||||
|
parser.error("--scratch and --draft cannot be both specfied")
|
||||||
activate_session(session, options)
|
activate_session(session, options)
|
||||||
|
|
||||||
target = args[0]
|
target = args[0]
|
||||||
|
|
@ -874,6 +882,8 @@ def handle_wrapper_rpm(options, session, args):
|
||||||
opts['skip_tag'] = True
|
opts['skip_tag'] = True
|
||||||
if build_opts.scratch:
|
if build_opts.scratch:
|
||||||
opts['scratch'] = True
|
opts['scratch'] = True
|
||||||
|
if build_opts.draft:
|
||||||
|
opts['draft'] = True
|
||||||
task_id = session.wrapperRPM(build_id, url, target, priority, opts=opts)
|
task_id = session.wrapperRPM(build_id, url, target, priority, opts=opts)
|
||||||
print("Created task: %d" % task_id)
|
print("Created task: %d" % task_id)
|
||||||
print("Task info: %s/taskinfo?taskID=%s" % (options.weburl, task_id))
|
print("Task info: %s/taskinfo?taskID=%s" % (options.weburl, task_id))
|
||||||
|
|
@ -1314,6 +1324,10 @@ def handle_import(goptions, session, args):
|
||||||
parser.add_option("--test", action="store_true", help="Don't actually import")
|
parser.add_option("--test", action="store_true", help="Don't actually import")
|
||||||
parser.add_option("--create-build", action="store_true", help="Auto-create builds as needed")
|
parser.add_option("--create-build", action="store_true", help="Auto-create builds as needed")
|
||||||
parser.add_option("--src-epoch", help="When auto-creating builds, use this epoch")
|
parser.add_option("--src-epoch", help="When auto-creating builds, use this epoch")
|
||||||
|
parser.add_option("--create-draft", action="store_true",
|
||||||
|
help="Auto-create draft builds instead as needed")
|
||||||
|
parser.add_option("--draft-build", metavar='NVR|ID',
|
||||||
|
help="The target draft build to import to")
|
||||||
(options, args) = parser.parse_args(args)
|
(options, args) = parser.parse_args(args)
|
||||||
if len(args) < 1:
|
if len(args) < 1:
|
||||||
parser.error("At least one package must be specified")
|
parser.error("At least one package must be specified")
|
||||||
|
|
@ -1324,6 +1338,35 @@ def handle_import(goptions, session, args):
|
||||||
options.src_epoch = int(options.src_epoch)
|
options.src_epoch = int(options.src_epoch)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
parser.error("Invalid value for epoch: %s" % options.src_epoch)
|
parser.error("Invalid value for epoch: %s" % options.src_epoch)
|
||||||
|
if options.create_draft:
|
||||||
|
print("Will create draft build instead if desired nvr doesn't exist")
|
||||||
|
options.create_build = True
|
||||||
|
draft_build = None
|
||||||
|
draft_target_nvr = None
|
||||||
|
if options.draft_build:
|
||||||
|
if options.create_build:
|
||||||
|
parser.error(
|
||||||
|
"To ensure no misuse, don't specify --draft-build while auto-creating."
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
draft_build = int(options.draft_build)
|
||||||
|
except ValueError:
|
||||||
|
draft_build = options.draft_build
|
||||||
|
draft_build = session.getBuild(draft_build)
|
||||||
|
if not draft_build:
|
||||||
|
error("No such build: %s" % options.draft_build)
|
||||||
|
if not draft_build.get('draft'):
|
||||||
|
error("%s is not a draft build" % draft_build['nvr'])
|
||||||
|
b_state = koji.BUILD_STATES[draft_build['state']]
|
||||||
|
if b_state != 'COMPLETE':
|
||||||
|
error("draft build %s is expected as COMPLETE, got %s" % (draft_build['nvr'], b_state))
|
||||||
|
target_release = draft_build.get('extra', {}).get('draft', {}).get('target_release')
|
||||||
|
if not target_release:
|
||||||
|
error("Invalid draft build: %s,"
|
||||||
|
" no draft.target_release found in extra" % draft_build['nvr'])
|
||||||
|
draft_target_nvr = "%s-%s-%s" % (
|
||||||
|
draft_build['name'], draft_build['version'], target_release
|
||||||
|
)
|
||||||
activate_session(session, goptions)
|
activate_session(session, goptions)
|
||||||
to_import = {}
|
to_import = {}
|
||||||
for path in args:
|
for path in args:
|
||||||
|
|
@ -1335,6 +1378,7 @@ def handle_import(goptions, session, args):
|
||||||
else:
|
else:
|
||||||
nvr = "%(name)s-%(version)s-%(release)s" % koji.parse_NVRA(data['sourcerpm'])
|
nvr = "%(name)s-%(version)s-%(release)s" % koji.parse_NVRA(data['sourcerpm'])
|
||||||
to_import.setdefault(nvr, []).append((path, data))
|
to_import.setdefault(nvr, []).append((path, data))
|
||||||
|
|
||||||
builds_missing = False
|
builds_missing = False
|
||||||
nvrs = sorted(to_list(to_import.keys()))
|
nvrs = sorted(to_list(to_import.keys()))
|
||||||
for nvr in nvrs:
|
for nvr in nvrs:
|
||||||
|
|
@ -1343,29 +1387,41 @@ def handle_import(goptions, session, args):
|
||||||
if data['sourcepackage']:
|
if data['sourcepackage']:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
if nvr == draft_target_nvr:
|
||||||
|
continue
|
||||||
|
# when no srpm and create_draft
|
||||||
|
elif options.create_draft:
|
||||||
|
print("Missing srpm for draft build creating with target nvr: %s" % nvr)
|
||||||
|
builds_missing = True
|
||||||
|
continue
|
||||||
# no srpm included, check for build
|
# no srpm included, check for build
|
||||||
binfo = session.getBuild(nvr)
|
binfo = session.getBuild(nvr)
|
||||||
if not binfo:
|
if not binfo:
|
||||||
print("Missing build or srpm: %s" % nvr)
|
print("Missing build or srpm: %s" % nvr)
|
||||||
builds_missing = True
|
builds_missing = True
|
||||||
if builds_missing and not options.create_build:
|
if builds_missing and (not options.create_build or options.create_draft):
|
||||||
print("Aborting import")
|
print("Aborting import")
|
||||||
return
|
return
|
||||||
|
|
||||||
# local function to help us out below
|
# local function to help us out below
|
||||||
def do_import(path, data):
|
def do_import(path, data, draft_build=None):
|
||||||
|
draft = bool(draft_build) or options.create_draft
|
||||||
rinfo = dict([(k, data[k]) for k in ('name', 'version', 'release', 'arch')])
|
rinfo = dict([(k, data[k]) for k in ('name', 'version', 'release', 'arch')])
|
||||||
prev = session.getRPM(rinfo)
|
if draft_build or not options.create_draft:
|
||||||
if prev and not prev.get('external_repo_id', 0):
|
opts = {}
|
||||||
if prev['payloadhash'] == koji.hex_string(data['sigmd5']):
|
if draft_build:
|
||||||
print("RPM already imported: %s" % path)
|
opts['build'] = draft_build
|
||||||
else:
|
prev = session.getRPM(rinfo, **opts)
|
||||||
warn("md5sum mismatch for %s" % path)
|
if prev and not prev.get('external_repo_id', 0):
|
||||||
warn(" A different rpm with the same name has already been imported")
|
if prev['payloadhash'] == koji.hex_string(data['sigmd5']):
|
||||||
warn(" Existing sigmd5 is %r, your import has %r" % (
|
print("RPM already imported: %s" % path)
|
||||||
prev['payloadhash'], koji.hex_string(data['sigmd5'])))
|
else:
|
||||||
print("Skipping import")
|
warn("md5sum mismatch for %s" % path)
|
||||||
return
|
warn(" A different rpm with the same name has already been imported")
|
||||||
|
warn(" Existing sigmd5 is %r, your import has %r" % (
|
||||||
|
prev['payloadhash'], koji.hex_string(data['sigmd5'])))
|
||||||
|
print("Skipping import")
|
||||||
|
return
|
||||||
if options.test:
|
if options.test:
|
||||||
print("Test mode -- skipping import for %s" % path)
|
print("Test mode -- skipping import for %s" % path)
|
||||||
return
|
return
|
||||||
|
|
@ -1381,41 +1437,69 @@ def handle_import(goptions, session, args):
|
||||||
sys.stdout.write("importing %s... " % path)
|
sys.stdout.write("importing %s... " % path)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
try:
|
try:
|
||||||
session.importRPM(serverdir, os.path.basename(path))
|
opts = {}
|
||||||
|
if draft_build:
|
||||||
|
opts['build'] = draft_build
|
||||||
|
if draft:
|
||||||
|
opts['draft'] = draft
|
||||||
|
rpminfo = session.importRPM(serverdir, os.path.basename(path), **opts)
|
||||||
except koji.GenericError as e:
|
except koji.GenericError as e:
|
||||||
|
rpminfo = None
|
||||||
print("\nError importing: %s" % str(e).splitlines()[-1])
|
print("\nError importing: %s" % str(e).splitlines()[-1])
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
else:
|
else:
|
||||||
print("done")
|
print("done")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
return rpminfo
|
||||||
|
|
||||||
for nvr in nvrs:
|
for nvr in nvrs:
|
||||||
# check for existing build
|
# check for existing build
|
||||||
need_build = True
|
need_build = True
|
||||||
binfo = session.getBuild(nvr)
|
is_draft = True
|
||||||
if binfo:
|
if nvr == draft_target_nvr:
|
||||||
b_state = koji.BUILD_STATES[binfo['state']]
|
binfo = draft_build
|
||||||
if b_state == 'COMPLETE':
|
need_build = False
|
||||||
need_build = False
|
elif options.create_draft:
|
||||||
elif b_state in ['FAILED', 'CANCELED']:
|
binfo = None
|
||||||
if not options.create_build:
|
need_build = True
|
||||||
print("Build %s state is %s. Skipping import" % (nvr, b_state))
|
else:
|
||||||
|
is_draft = False
|
||||||
|
binfo = session.getBuild(nvr)
|
||||||
|
if binfo:
|
||||||
|
b_state = koji.BUILD_STATES[binfo['state']]
|
||||||
|
if b_state == 'COMPLETE':
|
||||||
|
need_build = False
|
||||||
|
elif b_state in ['FAILED', 'CANCELED']:
|
||||||
|
if not options.create_build:
|
||||||
|
print("Build %s state is %s. Skipping import" % (nvr, b_state))
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
print("Build %s exists with state=%s. Skipping import" % (nvr, b_state))
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
print("Build %s exists with state=%s. Skipping import" % (nvr, b_state))
|
|
||||||
continue
|
|
||||||
|
|
||||||
# import srpms first, if any
|
# import srpms first, if any
|
||||||
for path, data in to_import[nvr]:
|
for path, data in to_import[nvr]:
|
||||||
if data['sourcepackage']:
|
if data['sourcepackage']:
|
||||||
if binfo and b_state != 'COMPLETE':
|
# we can not fix state for draft build by createEmptyBuild
|
||||||
|
if not is_draft and binfo and b_state != 'COMPLETE':
|
||||||
# need to fix the state
|
# need to fix the state
|
||||||
print("Creating empty build: %s" % nvr)
|
print("Creating empty build: %s" % nvr)
|
||||||
b_data = koji.util.dslice(binfo, ['name', 'version', 'release'])
|
b_data = koji.util.dslice(binfo, ['name', 'version', 'release'])
|
||||||
b_data['epoch'] = data['epoch']
|
b_data['epoch'] = data['epoch']
|
||||||
session.createEmptyBuild(**b_data)
|
session.createEmptyBuild(**b_data)
|
||||||
binfo = session.getBuild(nvr)
|
binfo = session.getBuild(nvr)
|
||||||
do_import(path, data)
|
dbld = binfo if is_draft else None
|
||||||
|
will_create = False
|
||||||
|
if options.create_draft and not dbld:
|
||||||
|
will_create = True
|
||||||
|
print("Will create draft build with target nvr: %s while importing" % nvr)
|
||||||
|
rpminfo = do_import(path, data, draft_build=dbld)
|
||||||
|
# only needed for draft build, but
|
||||||
|
# TODO: should be able to apply to regular import
|
||||||
|
if rpminfo and rpminfo.get('build', {}).get('draft'):
|
||||||
|
binfo = rpminfo['build']
|
||||||
|
if will_create:
|
||||||
|
print("Draft build: %s created" % binfo['nvr'])
|
||||||
need_build = False
|
need_build = False
|
||||||
|
|
||||||
if need_build:
|
if need_build:
|
||||||
|
|
@ -1424,11 +1508,11 @@ def handle_import(goptions, session, args):
|
||||||
if binfo:
|
if binfo:
|
||||||
# should have caught this earlier, but just in case...
|
# should have caught this earlier, but just in case...
|
||||||
b_state = koji.BUILD_STATES[binfo['state']]
|
b_state = koji.BUILD_STATES[binfo['state']]
|
||||||
print("Build %s state is %s. Skipping import" % (nvr, b_state))
|
print("Build %s state is %s. Skipping import" % (binfo['nvr'], b_state))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
print("No such build: %s (include matching srpm or use "
|
print("No such build: %s (include matching srpm or use "
|
||||||
"--create-build option to add it)" % nvr)
|
"--create-build/--create-draft option to add it)" % nvr)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# let's make a new build
|
# let's make a new build
|
||||||
|
|
@ -1439,17 +1523,22 @@ def handle_import(goptions, session, args):
|
||||||
# pull epoch from first rpm
|
# pull epoch from first rpm
|
||||||
data = to_import[nvr][0][1]
|
data = to_import[nvr][0][1]
|
||||||
b_data['epoch'] = data['epoch']
|
b_data['epoch'] = data['epoch']
|
||||||
if options.test:
|
if options.create_draft:
|
||||||
print("Test mode -- would have created empty build: %s" % nvr)
|
b_data['draft'] = True
|
||||||
|
b_display = "empty draft build with target nvr: %s" % nvr
|
||||||
else:
|
else:
|
||||||
print("Creating empty build: %s" % nvr)
|
b_display = "empty build: %s" % nvr
|
||||||
session.createEmptyBuild(**b_data)
|
if options.test:
|
||||||
binfo = session.getBuild(nvr)
|
print("Test mode -- would have created %s" % b_display)
|
||||||
|
else:
|
||||||
|
print("Creating %s" % b_display)
|
||||||
|
buildid = session.createEmptyBuild(**b_data)
|
||||||
|
binfo = session.getBuild(buildid)
|
||||||
|
|
||||||
for path, data in to_import[nvr]:
|
for path, data in to_import[nvr]:
|
||||||
if data['sourcepackage']:
|
if data['sourcepackage']:
|
||||||
continue
|
continue
|
||||||
do_import(path, data)
|
do_import(path, data, draft_build=binfo if is_draft else None)
|
||||||
|
|
||||||
|
|
||||||
def handle_import_cg(goptions, session, args):
|
def handle_import_cg(goptions, session, args):
|
||||||
|
|
@ -2727,11 +2816,15 @@ def anon_handle_list_tagged(goptions, session, args):
|
||||||
parser.add_option("--ts", type='int', metavar="TIMESTAMP",
|
parser.add_option("--ts", type='int', metavar="TIMESTAMP",
|
||||||
help="query at last event before timestamp")
|
help="query at last event before timestamp")
|
||||||
parser.add_option("--repo", type='int', metavar="REPO#", help="query at event for a repo")
|
parser.add_option("--repo", type='int', metavar="REPO#", help="query at event for a repo")
|
||||||
|
parser.add_option("--draft-only", action="store_true", help="Only list draft builds/rpms")
|
||||||
|
parser.add_option("--no-draft", action="store_true", help="Only list regular builds/rpms")
|
||||||
(options, args) = parser.parse_args(args)
|
(options, args) = parser.parse_args(args)
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
parser.error("A tag name must be specified")
|
parser.error("A tag name must be specified")
|
||||||
elif len(args) > 2:
|
elif len(args) > 2:
|
||||||
parser.error("Only one package name may be specified")
|
parser.error("Only one package name may be specified")
|
||||||
|
if options.no_draft and options.draft_only:
|
||||||
|
parser.error("--draft-only conflicts with --no-draft")
|
||||||
ensure_connection(session, goptions)
|
ensure_connection(session, goptions)
|
||||||
pathinfo = koji.PathInfo()
|
pathinfo = koji.PathInfo()
|
||||||
package = None
|
package = None
|
||||||
|
|
@ -2753,6 +2846,10 @@ def anon_handle_list_tagged(goptions, session, args):
|
||||||
options.rpms = True
|
options.rpms = True
|
||||||
if options.type:
|
if options.type:
|
||||||
opts['type'] = options.type
|
opts['type'] = options.type
|
||||||
|
elif options.no_draft:
|
||||||
|
opts['draft'] = koji.FLAG_REGULAR_BUILD
|
||||||
|
elif options.draft_only:
|
||||||
|
opts['draft'] = koji.FLAG_DRAFT_BUILD
|
||||||
event = koji.util.eventFromOpts(session, options)
|
event = koji.util.eventFromOpts(session, options)
|
||||||
event_id = None
|
event_id = None
|
||||||
if event:
|
if event:
|
||||||
|
|
@ -2798,7 +2895,9 @@ def anon_handle_list_tagged(goptions, session, args):
|
||||||
fmt = "%(path)s"
|
fmt = "%(path)s"
|
||||||
data = [x for x in data if 'path' in x]
|
data = [x for x in data if 'path' in x]
|
||||||
else:
|
else:
|
||||||
fmt = "%(name)s-%(version)s-%(release)s.%(arch)s"
|
fmt = "%(name)s-%(version)s-%(release)s.%(arch)s%(draft_suffix)s"
|
||||||
|
for x in data:
|
||||||
|
x['draft_suffix'] = (' (#draft_%s)' % x['build_id']) if x.get('draft') else ''
|
||||||
if options.sigs:
|
if options.sigs:
|
||||||
fmt = "%(sigkey)s " + fmt
|
fmt = "%(sigkey)s " + fmt
|
||||||
else:
|
else:
|
||||||
|
|
@ -2861,10 +2960,13 @@ def anon_handle_list_buildroot(goptions, session, args):
|
||||||
fmt = "%(nvr)s.%(arch)s"
|
fmt = "%(nvr)s.%(arch)s"
|
||||||
order = sorted([(fmt % x, x) for x in list_rpms])
|
order = sorted([(fmt % x, x) for x in list_rpms])
|
||||||
for nvra, rinfo in order:
|
for nvra, rinfo in order:
|
||||||
if options.verbose and rinfo.get('is_update'):
|
line = nvra
|
||||||
print("%s [update]" % nvra)
|
if options.verbose:
|
||||||
else:
|
if rinfo.get('draft'):
|
||||||
print(nvra)
|
line += " (#draft_%s)" % rinfo['build_id']
|
||||||
|
if rinfo.get('is_update'):
|
||||||
|
line += " [update]"
|
||||||
|
print(line)
|
||||||
|
|
||||||
list_archives = session.listArchives(**opts)
|
list_archives = session.listArchives(**opts)
|
||||||
if list_archives:
|
if list_archives:
|
||||||
|
|
@ -3403,6 +3505,8 @@ def anon_handle_list_builds(goptions, session, args):
|
||||||
parser.add_option("--source", help="Only builds where the source field matches (glob pattern)")
|
parser.add_option("--source", help="Only builds where the source field matches (glob pattern)")
|
||||||
parser.add_option("--owner", help="List builds built by this owner")
|
parser.add_option("--owner", help="List builds built by this owner")
|
||||||
parser.add_option("--volume", help="List builds by volume ID")
|
parser.add_option("--volume", help="List builds by volume ID")
|
||||||
|
parser.add_option("--draft-only", action="store_true", help="Only list draft builds")
|
||||||
|
parser.add_option("--no-draft", action="store_true", help="Only list regular builds")
|
||||||
parser.add_option("-k", "--sort-key", action="append", metavar='FIELD',
|
parser.add_option("-k", "--sort-key", action="append", metavar='FIELD',
|
||||||
default=[], help="Sort the list by the named field. Allowed sort keys: "
|
default=[], help="Sort the list by the named field. Allowed sort keys: "
|
||||||
"build_id, owner_name, state")
|
"build_id, owner_name, state")
|
||||||
|
|
@ -3419,6 +3523,12 @@ def anon_handle_list_builds(goptions, session, args):
|
||||||
value = getattr(options, key)
|
value = getattr(options, key)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
opts[key] = value
|
opts[key] = value
|
||||||
|
if options.no_draft and options.draft_only:
|
||||||
|
parser.error("--draft-only conflits with --no-draft")
|
||||||
|
elif options.no_draft:
|
||||||
|
opts['draft'] = koji.FLAG_REGULAR_BUILD
|
||||||
|
elif options.draft_only:
|
||||||
|
opts['draft'] = koji.FLAG_DRAFT_BUILD
|
||||||
if options.cg:
|
if options.cg:
|
||||||
opts['cgID'] = options.cg
|
opts['cgID'] = options.cg
|
||||||
if options.package:
|
if options.package:
|
||||||
|
|
@ -3520,13 +3630,24 @@ def anon_handle_rpminfo(goptions, session, args):
|
||||||
parser = OptionParser(usage=get_usage_str(usage))
|
parser = OptionParser(usage=get_usage_str(usage))
|
||||||
parser.add_option("--buildroots", action="store_true",
|
parser.add_option("--buildroots", action="store_true",
|
||||||
help="show buildroots the rpm was used in")
|
help="show buildroots the rpm was used in")
|
||||||
|
parser.add_option("--build", metavar="NVR|ID",
|
||||||
|
help="show the rpm(s) in the build")
|
||||||
|
|
||||||
(options, args) = parser.parse_args(args)
|
(options, args) = parser.parse_args(args)
|
||||||
if len(args) < 1:
|
if len(args) < 1:
|
||||||
parser.error("Please specify an RPM")
|
parser.error("Please specify an RPM")
|
||||||
|
opts = {}
|
||||||
|
build = options.build
|
||||||
|
if options.build:
|
||||||
|
try:
|
||||||
|
build = int(build)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
opts['build'] = build
|
||||||
ensure_connection(session, goptions)
|
ensure_connection(session, goptions)
|
||||||
error_hit = False
|
error_hit = False
|
||||||
for rpm in args:
|
for rpm in args:
|
||||||
info = session.getRPM(rpm)
|
info = session.getRPM(rpm, **opts)
|
||||||
if info is None:
|
if info is None:
|
||||||
warn("No such rpm: %s\n" % rpm)
|
warn("No such rpm: %s\n" % rpm)
|
||||||
error_hit = True
|
error_hit = True
|
||||||
|
|
@ -3536,24 +3657,29 @@ def anon_handle_rpminfo(goptions, session, args):
|
||||||
else:
|
else:
|
||||||
info['epoch'] = str(info['epoch']) + ":"
|
info['epoch'] = str(info['epoch']) + ":"
|
||||||
if not info.get('external_repo_id', 0):
|
if not info.get('external_repo_id', 0):
|
||||||
buildinfo = session.getBuild(info['build_id'])
|
if info['arch'] == 'src':
|
||||||
buildinfo['name'] = buildinfo['package_name']
|
srpminfo = info.copy()
|
||||||
buildinfo['arch'] = 'src'
|
|
||||||
if buildinfo['epoch'] is None:
|
|
||||||
buildinfo['epoch'] = ""
|
|
||||||
else:
|
else:
|
||||||
buildinfo['epoch'] = str(buildinfo['epoch']) + ":"
|
srpminfo = session.listRPMs(buildID=info['build_id'], arches='src')[0]
|
||||||
|
if srpminfo['epoch'] is None:
|
||||||
|
srpminfo['epoch'] = ""
|
||||||
|
else:
|
||||||
|
srpminfo['epoch'] = str(srpminfo['epoch']) + ":"
|
||||||
|
buildinfo = session.getBuild(info['build_id'])
|
||||||
print("RPM: %(epoch)s%(name)s-%(version)s-%(release)s.%(arch)s [%(id)d]" % info)
|
print("RPM: %(epoch)s%(name)s-%(version)s-%(release)s.%(arch)s [%(id)d]" % info)
|
||||||
|
if info.get('draft'):
|
||||||
|
print("Draft: YES")
|
||||||
if info.get('external_repo_id'):
|
if info.get('external_repo_id'):
|
||||||
repo = session.getExternalRepo(info['external_repo_id'])
|
repo = session.getExternalRepo(info['external_repo_id'])
|
||||||
print("External Repository: %(name)s [%(id)i]" % repo)
|
print("External Repository: %(name)s [%(id)i]" % repo)
|
||||||
print("External Repository url: %(url)s" % repo)
|
print("External Repository url: %(url)s" % repo)
|
||||||
else:
|
else:
|
||||||
|
print("Build: %(nvr)s [%(id)d]" % buildinfo)
|
||||||
print("RPM Path: %s" %
|
print("RPM Path: %s" %
|
||||||
os.path.join(koji.pathinfo.build(buildinfo), koji.pathinfo.rpm(info)))
|
os.path.join(koji.pathinfo.build(buildinfo), koji.pathinfo.rpm(info)))
|
||||||
print("SRPM: %(epoch)s%(name)s-%(version)s-%(release)s [%(id)d]" % buildinfo)
|
print("SRPM: %(epoch)s%(name)s-%(version)s-%(release)s [%(id)d]" % srpminfo)
|
||||||
print("SRPM Path: %s" %
|
print("SRPM Path: %s" %
|
||||||
os.path.join(koji.pathinfo.build(buildinfo), koji.pathinfo.rpm(buildinfo)))
|
os.path.join(koji.pathinfo.build(buildinfo), koji.pathinfo.rpm(srpminfo)))
|
||||||
print("Built: %s" % time.strftime('%a, %d %b %Y %H:%M:%S %Z',
|
print("Built: %s" % time.strftime('%a, %d %b %Y %H:%M:%S %Z',
|
||||||
time.localtime(info['buildtime'])))
|
time.localtime(info['buildtime'])))
|
||||||
print("SIGMD5: %(payloadhash)s" % info)
|
print("SIGMD5: %(payloadhash)s" % info)
|
||||||
|
|
@ -3563,6 +3689,7 @@ def anon_handle_rpminfo(goptions, session, args):
|
||||||
headers=["license"])
|
headers=["license"])
|
||||||
if 'license' in headers:
|
if 'license' in headers:
|
||||||
print("License: %(license)s" % headers)
|
print("License: %(license)s" % headers)
|
||||||
|
# kept for backward compatibility
|
||||||
print("Build ID: %(build_id)s" % info)
|
print("Build ID: %(build_id)s" % info)
|
||||||
if info['buildroot_id'] is None:
|
if info['buildroot_id'] is None:
|
||||||
print("No buildroot data available")
|
print("No buildroot data available")
|
||||||
|
|
@ -3619,6 +3746,8 @@ def anon_handle_buildinfo(goptions, session, args):
|
||||||
info['arch'] = 'src'
|
info['arch'] = 'src'
|
||||||
info['state'] = koji.BUILD_STATES[info['state']]
|
info['state'] = koji.BUILD_STATES[info['state']]
|
||||||
print("BUILD: %(name)s-%(version)s-%(release)s [%(id)d]" % info)
|
print("BUILD: %(name)s-%(version)s-%(release)s [%(id)d]" % info)
|
||||||
|
if info.get('draft'):
|
||||||
|
print("Draft: YES")
|
||||||
print("State: %(state)s" % info)
|
print("State: %(state)s" % info)
|
||||||
if info['state'] == 'BUILDING':
|
if info['state'] == 'BUILDING':
|
||||||
print("Reserved by: %(cg_name)s" % info)
|
print("Reserved by: %(cg_name)s" % info)
|
||||||
|
|
|
||||||
|
|
@ -925,3 +925,10 @@ class DatetimeJSONEncoder(json.JSONEncoder):
|
||||||
if isinstance(o, xmlrpc_client.DateTime):
|
if isinstance(o, xmlrpc_client.DateTime):
|
||||||
return str(o)
|
return str(o)
|
||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
||||||
|
def yesno(x):
|
||||||
|
if x:
|
||||||
|
return 'Y'
|
||||||
|
else:
|
||||||
|
return 'N'
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ from __future__ import absolute_import, division
|
||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
import errno
|
import errno
|
||||||
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -274,6 +275,7 @@ TAG_UPDATE_TYPES = Enum((
|
||||||
'VOLUME_CHANGE',
|
'VOLUME_CHANGE',
|
||||||
'IMPORT',
|
'IMPORT',
|
||||||
'MANUAL',
|
'MANUAL',
|
||||||
|
'DRAFT_PROMOTION',
|
||||||
))
|
))
|
||||||
|
|
||||||
# BEGIN kojikamid dup #
|
# BEGIN kojikamid dup #
|
||||||
|
|
@ -295,6 +297,44 @@ PRIO_DEFAULT = 20
|
||||||
DEFAULT_REQUEST_TIMEOUT = 60 * 60 * 12
|
DEFAULT_REQUEST_TIMEOUT = 60 * 60 * 12
|
||||||
DEFAULT_AUTH_TIMEOUT = 60
|
DEFAULT_AUTH_TIMEOUT = 60
|
||||||
|
|
||||||
|
# draft release format
|
||||||
|
DRAFT_RELEASE_FORMAT = '{release}#draft_{id}'
|
||||||
|
|
||||||
|
FLAG_DRAFT_BUILD = 1
|
||||||
|
FLAG_REGULAR_BUILD = 2
|
||||||
|
FLAG_ALL_BUILD = FLAG_DRAFT_BUILD | FLAG_REGULAR_BUILD
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
from enum import IntFlag
|
||||||
|
|
||||||
|
# draft build bit FLAGs
|
||||||
|
class DRAFT_FLAG(IntFlag):
|
||||||
|
|
||||||
|
DRAFT = FLAG_DRAFT_BUILD
|
||||||
|
REGULAR = FLAG_REGULAR_BUILD
|
||||||
|
ALL = FLAG_ALL_BUILD
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _missing_(cls, value):
|
||||||
|
if not isinstance(value, int):
|
||||||
|
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
|
||||||
|
# diable member creation and negative integer
|
||||||
|
return cls._value2member_map_.get(value)
|
||||||
|
|
||||||
|
def convert_draft_option(func=None, kw='draft'):
|
||||||
|
def wrapper(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def convert(*args, **kwargs):
|
||||||
|
if kw in kwargs:
|
||||||
|
kwargs[kw] = DRAFT_FLAG(kwargs[kw])
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return convert
|
||||||
|
if func is None:
|
||||||
|
return wrapper
|
||||||
|
else:
|
||||||
|
return wrapper(func)
|
||||||
|
|
||||||
|
|
||||||
# BEGIN kojikamid dup #
|
# BEGIN kojikamid dup #
|
||||||
|
|
||||||
# Exceptions
|
# Exceptions
|
||||||
|
|
@ -2363,6 +2403,39 @@ class PathInfo(object):
|
||||||
return self.volumedir(build.get('volume_name')) + \
|
return self.volumedir(build.get('volume_name')) + \
|
||||||
("/packages/%(name)s/%(version)s/%(release)s" % build)
|
("/packages/%(name)s/%(version)s/%(release)s" % build)
|
||||||
|
|
||||||
|
def to_buildinfo(self, path):
|
||||||
|
"""Revert build dir path (<topdir>/[<volumedir>/]packages/<name>/<version>/<release>)
|
||||||
|
to build map with the data below:
|
||||||
|
|
||||||
|
- name
|
||||||
|
- version
|
||||||
|
- release
|
||||||
|
- volume_name
|
||||||
|
"""
|
||||||
|
path = path.rstrip('/')
|
||||||
|
parts = path.rsplit('/', 4)
|
||||||
|
if len(parts) != 5:
|
||||||
|
raise GenericError("Invalid build path: %s" % path)
|
||||||
|
if parts[-4] != 'packages':
|
||||||
|
raise GenericError("Invalid build path: %s, packages subdir not found" % path)
|
||||||
|
nvr = parts[-3:]
|
||||||
|
rest = parts[0]
|
||||||
|
if not rest.startswith(self.topdir):
|
||||||
|
raise GenericError("Invalid build path: %s, topdir is not %r" % (path, self.topdir))
|
||||||
|
vol_part = rest[len(self.topdir):]
|
||||||
|
if not vol_part:
|
||||||
|
vol = 'DEFAULT'
|
||||||
|
elif vol_part.startswith('/vol/'):
|
||||||
|
vol = vol_part[5:]
|
||||||
|
else:
|
||||||
|
raise GenericError(
|
||||||
|
"Invalid build path: %s, volume dir: %s is incorrect" % (path, vol_part)
|
||||||
|
)
|
||||||
|
return {'name': nvr[0],
|
||||||
|
'version': nvr[1],
|
||||||
|
'release': nvr[2],
|
||||||
|
'volume_name': vol}
|
||||||
|
|
||||||
def mavenbuild(self, build):
|
def mavenbuild(self, build):
|
||||||
"""Return the directory where the Maven build exists in the global store
|
"""Return the directory where the Maven build exists in the global store
|
||||||
(/mnt/koji/packages)"""
|
(/mnt/koji/packages)"""
|
||||||
|
|
@ -2410,6 +2483,27 @@ class PathInfo(object):
|
||||||
"""Return the path (relative to build_dir) where an rpm belongs"""
|
"""Return the path (relative to build_dir) where an rpm belongs"""
|
||||||
return "%(arch)s/%(name)s-%(version)s-%(release)s.%(arch)s.rpm" % rpminfo
|
return "%(arch)s/%(name)s-%(version)s-%(release)s.%(arch)s.rpm" % rpminfo
|
||||||
|
|
||||||
|
def to_rpminfo(self, path, full=False):
|
||||||
|
"""Revert rpm path in build dir to rpm nvra map with/without build dir"""
|
||||||
|
if path.endswith('/'):
|
||||||
|
raise GenericError("Invalid path: %s, cannot be a directory" % path)
|
||||||
|
parts = path.rsplit('/', 2)
|
||||||
|
if full:
|
||||||
|
if len(parts) != 3:
|
||||||
|
raise GenericError("No build dir in path: %s" % path)
|
||||||
|
return self.to_buildinfo(parts[-3]), self._to_rpminfo(parts[-2:])
|
||||||
|
return self._to_rpminfo(parts)
|
||||||
|
|
||||||
|
def _to_rpminfo(self, parts):
|
||||||
|
if len(parts) != 2 or not parts[0]:
|
||||||
|
raise GenericError("Invalid path: %s" % '/'.join(parts))
|
||||||
|
rpminfo = parse_NVRA(parts[-1])
|
||||||
|
if parts[0] != rpminfo['arch']:
|
||||||
|
raise GenericError(
|
||||||
|
'mismatch between arch dir (%s) and arch (%s) in rpm' % (parts[0], rpminfo)
|
||||||
|
)
|
||||||
|
return rpminfo
|
||||||
|
|
||||||
def signed(self, rpminfo, sigkey):
|
def signed(self, rpminfo, sigkey):
|
||||||
"""Return the path (relative to build dir) where a signed rpm lives"""
|
"""Return the path (relative to build dir) where a signed rpm lives"""
|
||||||
return "data/signed/%s/" % sigkey + self.rpm(rpminfo)
|
return "data/signed/%s/" % sigkey + self.rpm(rpminfo)
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ callbacks = {
|
||||||
'postRepoInit': [],
|
'postRepoInit': [],
|
||||||
'preRepoDone': [],
|
'preRepoDone': [],
|
||||||
'postRepoDone': [],
|
'postRepoDone': [],
|
||||||
|
'preBuildPromote': [],
|
||||||
|
'postBuildPromote': [],
|
||||||
'preCommit': [],
|
'preCommit': [],
|
||||||
'postCommit': [],
|
'postCommit': [],
|
||||||
# builder
|
# builder
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -610,6 +610,11 @@ _default_policies = {
|
||||||
'priority': '''
|
'priority': '''
|
||||||
all :: stay
|
all :: stay
|
||||||
''',
|
''',
|
||||||
|
'draft_promotion': '''
|
||||||
|
has_perm draft-promoter :: allow
|
||||||
|
is_build_owner :: allow
|
||||||
|
all :: deny Only draft-promoter and build owner can do this via default policy
|
||||||
|
'''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,22 @@ def prep_repo_done(cbtype, *args, **kws):
|
||||||
queue_msg(address, props, kws)
|
queue_msg(address, props, kws)
|
||||||
|
|
||||||
|
|
||||||
|
@convert_datetime
|
||||||
|
@callback('postBuildPromote')
|
||||||
|
def prep_build_promote(cbtype, *args, **kws):
|
||||||
|
kws['build'] = _strip_extra(kws['build'])
|
||||||
|
address = 'build.promote'
|
||||||
|
props = {'type': cbtype[4:],
|
||||||
|
'build_id': kws['build']['id'],
|
||||||
|
'name': kws['build']['name'],
|
||||||
|
'version': kws['build']['version'],
|
||||||
|
'release': kws['build']['release'],
|
||||||
|
'draft_release': kws['draft_release'],
|
||||||
|
'target_release': kws['target_release'],
|
||||||
|
'user': kws['user']['name']}
|
||||||
|
queue_msg(address, props, kws)
|
||||||
|
|
||||||
|
|
||||||
def _send_msgs(urls, msgs, CONFIG):
|
def _send_msgs(urls, msgs, CONFIG):
|
||||||
random.shuffle(urls)
|
random.shuffle(urls)
|
||||||
for url in urls:
|
for url in urls:
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
-- from version 1.33 to 1.34
|
-- from version 1.33 to 1.34
|
||||||
|
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
||||||
-- scheduler tables
|
-- scheduler tables
|
||||||
CREATE TABLE scheduler_task_runs (
|
CREATE TABLE scheduler_task_runs (
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
id SERIAL NOT NULL PRIMARY KEY,
|
||||||
|
|
@ -48,4 +49,27 @@ BEGIN;
|
||||||
) WITHOUT OIDS;
|
) WITHOUT OIDS;
|
||||||
|
|
||||||
INSERT INTO locks(name) VALUES('scheduler');
|
INSERT INTO locks(name) VALUES('scheduler');
|
||||||
|
|
||||||
|
-- draft builds
|
||||||
|
INSERT INTO permissions (name, description) VALUES ('draft-promoter', 'The permission required in the default "draft_promotion" hub policy rule to promote draft build.');
|
||||||
|
|
||||||
|
ALTER TABLE build ADD COLUMN draft BOOLEAN NOT NULL DEFAULT 'false';
|
||||||
|
ALTER TABLE build ADD CONSTRAINT draft_for_rpminfo UNIQUE (id, draft);
|
||||||
|
ALTER TABLE build ADD CONSTRAINT draft_release_sane CHECK
|
||||||
|
((draft AND release ~ ('^.*#draft_' || id::TEXT || '$'))
|
||||||
|
OR NOT draft);
|
||||||
|
|
||||||
|
ALTER TABLE rpminfo ADD COLUMN draft BOOLEAN;
|
||||||
|
ALTER TABLE rpminfo DROP CONSTRAINT rpminfo_build_id_fkey;
|
||||||
|
ALTER TABLE rpminfo ADD CONSTRAINT rpminfo_build_id_draft_fkey
|
||||||
|
FOREIGN KEY (build_id, draft) REFERENCES build(id, draft)
|
||||||
|
ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE rpminfo DROP CONSTRAINT rpminfo_unique_nvra;
|
||||||
|
ALTER TABLE rpminfo ADD CONSTRAINT build_id_draft_external_repo_id_sane
|
||||||
|
CHECK ((draft IS NULL AND build_id IS NULL AND external_repo_id <> 0)
|
||||||
|
OR (draft IS NOT NULL AND build_id IS NOT NULL AND external_repo_id = 0));
|
||||||
|
CREATE UNIQUE INDEX rpminfo_unique_nvra_not_draft
|
||||||
|
ON rpminfo(name,version,release,arch,external_repo_id)
|
||||||
|
WHERE draft IS NOT TRUE;
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ INSERT INTO permissions (name, description) VALUES ('tag', 'Manage packages in t
|
||||||
INSERT INTO permissions (name, description) VALUES ('target', 'Add, edit, and remove targets.');
|
INSERT INTO permissions (name, description) VALUES ('target', 'Add, edit, and remove targets.');
|
||||||
INSERT INTO permissions (name, description) VALUES ('win-admin', 'The default hub policy rule for "vm" requires this permission to trigger Windows builds.');
|
INSERT INTO permissions (name, description) VALUES ('win-admin', 'The default hub policy rule for "vm" requires this permission to trigger Windows builds.');
|
||||||
INSERT INTO permissions (name, description) VALUES ('win-import', 'Import win archives.');
|
INSERT INTO permissions (name, description) VALUES ('win-import', 'Import win archives.');
|
||||||
|
INSERT INTO permissions (name, description) VALUES ('draft-promoter', 'The permission required in the default "draft_promotion" hub policy rule to promote draft build.');
|
||||||
|
|
||||||
CREATE TABLE user_perms (
|
CREATE TABLE user_perms (
|
||||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||||
|
|
@ -279,11 +280,12 @@ CREATE TABLE content_generator (
|
||||||
-- null, or may point to a deleted task.
|
-- null, or may point to a deleted task.
|
||||||
CREATE TABLE build (
|
CREATE TABLE build (
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
id SERIAL NOT NULL PRIMARY KEY,
|
||||||
volume_id INTEGER NOT NULL REFERENCES volume (id),
|
volume_id INTEGER NOT NULL REFERENCES volume (id),
|
||||||
pkg_id INTEGER NOT NULL REFERENCES package (id) DEFERRABLE,
|
pkg_id INTEGER NOT NULL REFERENCES package (id) DEFERRABLE,
|
||||||
version TEXT NOT NULL,
|
version TEXT NOT NULL,
|
||||||
release TEXT NOT NULL,
|
release TEXT NOT NULL,
|
||||||
epoch INTEGER,
|
epoch INTEGER,
|
||||||
|
draft BOOLEAN NOT NULL DEFAULT 'false',
|
||||||
source TEXT,
|
source TEXT,
|
||||||
create_event INTEGER NOT NULL REFERENCES events(id) DEFAULT get_event(),
|
create_event INTEGER NOT NULL REFERENCES events(id) DEFAULT get_event(),
|
||||||
start_time TIMESTAMPTZ,
|
start_time TIMESTAMPTZ,
|
||||||
|
|
@ -294,8 +296,11 @@ CREATE TABLE build (
|
||||||
cg_id INTEGER REFERENCES content_generator(id),
|
cg_id INTEGER REFERENCES content_generator(id),
|
||||||
extra TEXT,
|
extra TEXT,
|
||||||
CONSTRAINT build_pkg_ver_rel UNIQUE (pkg_id, version, release),
|
CONSTRAINT build_pkg_ver_rel UNIQUE (pkg_id, version, release),
|
||||||
|
CONSTRAINT draft_for_rpminfo UNIQUE (id, draft),
|
||||||
CONSTRAINT completion_sane CHECK ((state = 0 AND completion_time IS NULL) OR
|
CONSTRAINT completion_sane CHECK ((state = 0 AND completion_time IS NULL) OR
|
||||||
(state != 0 AND completion_time IS NOT NULL))
|
(state <> 0 AND completion_time IS NOT NULL)),
|
||||||
|
CONSTRAINT draft_release_sane CHECK ((draft AND release ~ ('^.*#draft_' || id::TEXT || '$')) OR
|
||||||
|
NOT draft)
|
||||||
) WITHOUT OIDS;
|
) WITHOUT OIDS;
|
||||||
|
|
||||||
CREATE INDEX build_by_pkg_id ON build (pkg_id);
|
CREATE INDEX build_by_pkg_id ON build (pkg_id);
|
||||||
|
|
@ -721,22 +726,28 @@ CREATE TABLE group_package_listing (
|
||||||
-- we don't store filename b/c filename should be N-V-R.A.rpm
|
-- we don't store filename b/c filename should be N-V-R.A.rpm
|
||||||
CREATE TABLE rpminfo (
|
CREATE TABLE rpminfo (
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
id SERIAL NOT NULL PRIMARY KEY,
|
||||||
build_id INTEGER REFERENCES build (id),
|
build_id INTEGER,
|
||||||
buildroot_id INTEGER REFERENCES buildroot (id),
|
buildroot_id INTEGER REFERENCES buildroot (id),
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
version TEXT NOT NULL,
|
version TEXT NOT NULL,
|
||||||
release TEXT NOT NULL,
|
release TEXT NOT NULL,
|
||||||
epoch INTEGER,
|
epoch INTEGER,
|
||||||
arch VARCHAR(16) NOT NULL,
|
arch VARCHAR(16) NOT NULL,
|
||||||
|
draft BOOLEAN,
|
||||||
external_repo_id INTEGER NOT NULL REFERENCES external_repo(id),
|
external_repo_id INTEGER NOT NULL REFERENCES external_repo(id),
|
||||||
payloadhash TEXT NOT NULL,
|
payloadhash TEXT NOT NULL,
|
||||||
size BIGINT NOT NULL,
|
size BIGINT NOT NULL,
|
||||||
buildtime BIGINT NOT NULL,
|
buildtime BIGINT NOT NULL,
|
||||||
metadata_only BOOLEAN NOT NULL DEFAULT FALSE,
|
metadata_only BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
extra TEXT,
|
extra TEXT,
|
||||||
CONSTRAINT rpminfo_unique_nvra UNIQUE (name,version,release,arch,external_repo_id)
|
FOREIGN KEY (build_id, draft) REFERENCES build (id, draft) ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT build_id_draft_external_repo_id_sane CHECK (
|
||||||
|
(draft IS NULL AND build_id IS NULL AND external_repo_id <> 0)
|
||||||
|
OR (draft IS NOT NULL AND build_id IS NOT NULL AND external_repo_id = 0))
|
||||||
) WITHOUT OIDS;
|
) WITHOUT OIDS;
|
||||||
CREATE INDEX rpminfo_build ON rpminfo(build_id);
|
CREATE INDEX rpminfo_build ON rpminfo(build_id);
|
||||||
|
CREATE UNIQUE INDEX rpminfo_unique_nvra_not_draft ON rpminfo(name,version,release,arch,external_repo_id)
|
||||||
|
WHERE draft IS NOT TRUE;
|
||||||
-- index for default search method for rpms, PG11+ can benefit from new include method
|
-- index for default search method for rpms, PG11+ can benefit from new include method
|
||||||
DO $$
|
DO $$
|
||||||
DECLARE version integer;
|
DECLARE version integer;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ X-Koji-Tag: f23
|
||||||
X-Koji-Package: sisu
|
X-Koji-Package: sisu
|
||||||
X-Koji-Builder: user
|
X-Koji-Builder: user
|
||||||
X-Koji-Status: complete
|
X-Koji-Status: complete
|
||||||
|
X-Koji-Draft: False
|
||||||
|
|
||||||
Package: sisu-0.3.0-0.2.M1.fc23
|
Package: sisu-0.3.0-0.2.M1.fc23
|
||||||
Tag: f23
|
Tag: f23
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
"completion_ts": 1424271457.10787,
|
"completion_ts": 1424271457.10787,
|
||||||
"id": 612609,
|
"id": 612609,
|
||||||
"volume_name": "DEFAULT",
|
"volume_name": "DEFAULT",
|
||||||
"nvr": "sisu-0.3.0-0.2.M1.fc23"
|
"nvr": "sisu-0.3.0-0.2.M1.fc23",
|
||||||
|
"draft": false
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
"dest_tag": 292,
|
"dest_tag": 292,
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,7 @@ Options:
|
||||||
Provide a JSON string of custom metadata to be
|
Provide a JSON string of custom metadata to be
|
||||||
deserialized and stored under the build's
|
deserialized and stored under the build's
|
||||||
extra.custom_user_metadata field
|
extra.custom_user_metadata field
|
||||||
|
--draft Build draft build instead
|
||||||
""" % (self.progname, self.progname))
|
""" % (self.progname, self.progname))
|
||||||
|
|
||||||
# Finally, assert that things were called as we expected.
|
# Finally, assert that things were called as we expected.
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,37 @@ Volume: DEFAULT
|
||||||
Task: 8 build (target, src)
|
Task: 8 build (target, src)
|
||||||
Finished: Thu, 04 Mar 2021 14:45:40 UTC
|
Finished: Thu, 04 Mar 2021 14:45:40 UTC
|
||||||
Tags:
|
Tags:
|
||||||
|
"""
|
||||||
|
anon_handle_buildinfo(self.options, self.session, [build])
|
||||||
|
self.assert_console_message(stdout, expected_stdout)
|
||||||
|
self.session.listTags.assert_called_once_with(build)
|
||||||
|
self.session.getBuild.assert_called_once_with(build)
|
||||||
|
self.session.getTaskInfo.assert_called_once_with(self.buildinfo['task_id'], request=True)
|
||||||
|
self.session.getMavenBuild.assert_called_once_with(self.buildinfo['id'])
|
||||||
|
self.session.getWinBuild.assert_called_once_with(self.buildinfo['id'])
|
||||||
|
self.session.listRPMs.assert_called_once_with(buildID=self.buildinfo['id'])
|
||||||
|
self.assertEqual(self.session.listArchives.call_count, 4)
|
||||||
|
|
||||||
|
@mock.patch('sys.stdout', new_callable=StringIO)
|
||||||
|
def test_buildinfo_draft(self, stdout):
|
||||||
|
build = 'test-build-1-1'
|
||||||
|
binfo = copy.deepcopy(self.buildinfo)
|
||||||
|
binfo['draft'] = True
|
||||||
|
self.session.getBuild.return_value = binfo
|
||||||
|
self.session.getTaskInfo.return_value = self.taskinfo
|
||||||
|
self.session.listTags.return_value = []
|
||||||
|
self.session.getMavenBuild.return_value = None
|
||||||
|
self.session.getWinBuild.return_value = None
|
||||||
|
self.session.listArchives.return_value = []
|
||||||
|
self.session.listRPMs.return_value = []
|
||||||
|
expected_stdout = """BUILD: test-build-1-1 [1]
|
||||||
|
Draft: YES
|
||||||
|
State: COMPLETE
|
||||||
|
Built by: kojiadmin
|
||||||
|
Volume: DEFAULT
|
||||||
|
Task: 8 build (target, src)
|
||||||
|
Finished: Thu, 04 Mar 2021 14:45:40 UTC
|
||||||
|
Tags:
|
||||||
"""
|
"""
|
||||||
anon_handle_buildinfo(self.options, self.session, [build])
|
anon_handle_buildinfo(self.options, self.session, [build])
|
||||||
self.assert_console_message(stdout, expected_stdout)
|
self.assert_console_message(stdout, expected_stdout)
|
||||||
|
|
|
||||||
|
|
@ -87,15 +87,22 @@ class TestImport(utils.CliTestCase):
|
||||||
def __do_import_test(self, options, session, arguments, **kwargs):
|
def __do_import_test(self, options, session, arguments, **kwargs):
|
||||||
expected = kwargs.get('expected', None)
|
expected = kwargs.get('expected', None)
|
||||||
rpm_header = kwargs.get('rpm_header', {})
|
rpm_header = kwargs.get('rpm_header', {})
|
||||||
|
rpm_headers = kwargs.get('rpm_headers', [])
|
||||||
|
if not rpm_headers:
|
||||||
|
rpm_headers = [rpm_header]
|
||||||
fake_srv_path = kwargs.get('srv_path', '/path/to/server/import')
|
fake_srv_path = kwargs.get('srv_path', '/path/to/server/import')
|
||||||
upload_rpm_mock = kwargs.get('upload_rpm_mock', session.uploadWrapper)
|
upload_rpm_mock = kwargs.get('upload_rpm_mock', session.uploadWrapper)
|
||||||
|
getrpm_called = kwargs.get('getrpm_called', True)
|
||||||
|
getrpm_calls = kwargs.get('getrpm_calls', [])
|
||||||
|
import_opts = kwargs.get('import_opts', {})
|
||||||
|
import_rpm_calls = kwargs.get('import_rpm_calls', None)
|
||||||
|
|
||||||
with mock.patch('koji.get_header_fields') as get_header_fields_mock:
|
with mock.patch('koji.get_header_fields') as get_header_fields_mock:
|
||||||
with mock.patch('koji_cli.commands.unique_path') as unique_path_mock:
|
with mock.patch('koji_cli.commands.unique_path') as unique_path_mock:
|
||||||
with mock.patch('koji_cli.commands.activate_session') as activate_session_mock:
|
with mock.patch('koji_cli.commands.activate_session') as activate_session_mock:
|
||||||
with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
|
with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
|
||||||
with upload_rpm_mock:
|
with upload_rpm_mock:
|
||||||
get_header_fields_mock.return_value = rpm_header
|
get_header_fields_mock.side_effect = rpm_headers
|
||||||
unique_path_mock.return_value = fake_srv_path
|
unique_path_mock.return_value = fake_srv_path
|
||||||
handle_import(options, session, arguments)
|
handle_import(options, session, arguments)
|
||||||
|
|
||||||
|
|
@ -104,21 +111,33 @@ class TestImport(utils.CliTestCase):
|
||||||
|
|
||||||
# check mock calls
|
# check mock calls
|
||||||
activate_session_mock.assert_called_with(session, options)
|
activate_session_mock.assert_called_with(session, options)
|
||||||
get_header_fields_mock.assert_called_with(
|
|
||||||
arguments[0],
|
get_header_fields_calls = [
|
||||||
('name', 'version', 'release', 'epoch',
|
mock.call(arguments[i],
|
||||||
'arch', 'sigmd5', 'sourcepackage', 'sourcerpm')
|
('name', 'version', 'release', 'epoch',
|
||||||
)
|
'arch', 'sigmd5', 'sourcepackage', 'sourcerpm')
|
||||||
|
) for i in range(len(rpm_headers) - 1)
|
||||||
|
]
|
||||||
|
|
||||||
session.getRPM.assert_called_with(
|
get_header_fields_mock.assert_has_calls(get_header_fields_calls)
|
||||||
dict((k, rpm_header.get(k, ''))
|
|
||||||
for k in ['release', 'version', 'arch', 'name'])
|
if getrpm_calls:
|
||||||
)
|
session.getRPM.assert_has_calls(getrpm_calls)
|
||||||
|
elif getrpm_called:
|
||||||
|
session.getRPM.assert_called_with(
|
||||||
|
dict((k, rpm_header.get(k, ''))
|
||||||
|
for k in ['release', 'version', 'arch', 'name'])
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
session.getRPM.assert_not_called()
|
||||||
|
|
||||||
unique_path_mock.assert_called_with('cli-import')
|
unique_path_mock.assert_called_with('cli-import')
|
||||||
upload_rpm_mock.assert_called_with(arguments[0], self.fake_srv_dir)
|
upload_rpm_mock.assert_called_with(arguments[0], self.fake_srv_dir)
|
||||||
session.importRPM.assert_called_with(
|
if import_rpm_calls:
|
||||||
self.fake_srv_dir, os.path.basename(arguments[0]))
|
session.importRPM.assert_has_calls(import_rpm_calls)
|
||||||
|
else:
|
||||||
|
session.importRPM.assert_called_with(
|
||||||
|
self.fake_srv_dir, os.path.basename(arguments[0]), **import_opts)
|
||||||
|
|
||||||
# reset for next test
|
# reset for next test
|
||||||
activate_session_mock.reset_mock()
|
activate_session_mock.reset_mock()
|
||||||
|
|
@ -136,6 +155,7 @@ class TestImport(utils.CliTestCase):
|
||||||
expected = kwargs.get('expected', None)
|
expected = kwargs.get('expected', None)
|
||||||
expected_warn = kwargs.get('expected_warn', None)
|
expected_warn = kwargs.get('expected_warn', None)
|
||||||
rpm_header = kwargs.get('rpm_header', {})
|
rpm_header = kwargs.get('rpm_header', {})
|
||||||
|
getrpm_called = kwargs.get('getrpm_called', True)
|
||||||
|
|
||||||
with mock.patch('koji.get_header_fields') as get_header_fields_mock:
|
with mock.patch('koji.get_header_fields') as get_header_fields_mock:
|
||||||
get_header_fields_mock.return_value = rpm_header
|
get_header_fields_mock.return_value = rpm_header
|
||||||
|
|
@ -152,11 +172,13 @@ class TestImport(utils.CliTestCase):
|
||||||
('name', 'version', 'release', 'epoch',
|
('name', 'version', 'release', 'epoch',
|
||||||
'arch', 'sigmd5', 'sourcepackage', 'sourcerpm')
|
'arch', 'sigmd5', 'sourcepackage', 'sourcerpm')
|
||||||
)
|
)
|
||||||
|
if getrpm_called:
|
||||||
session.getRPM.assert_called_with(
|
session.getRPM.assert_called_with(
|
||||||
dict((k, rpm_header.get(k, ''))
|
dict((k, rpm_header.get(k, ''))
|
||||||
for k in ['release', 'version', 'arch', 'name'])
|
for k in ['release', 'version', 'arch', 'name'])
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
session.getRPM.assert_not_called()
|
||||||
|
|
||||||
session.uploadWrapper.assert_not_called()
|
session.uploadWrapper.assert_not_called()
|
||||||
session.importRPM.assert_not_called()
|
session.importRPM.assert_not_called()
|
||||||
|
|
@ -677,6 +699,225 @@ class TestImport(utils.CliTestCase):
|
||||||
activate_session=None)
|
activate_session=None)
|
||||||
activate_session_mock.assert_not_called()
|
activate_session_mock.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
|
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||||
|
@mock.patch('koji_cli.commands.activate_session')
|
||||||
|
def test_handle_import_src_rpm_with_create_draft(
|
||||||
|
self,
|
||||||
|
activate_session_mock,
|
||||||
|
stderr,
|
||||||
|
stdout):
|
||||||
|
"""Test handle_import SRPM import with creating draft build case."""
|
||||||
|
arguments = ['/path/to/bash-4.4.12-5.fc26.rpm', '--create-draft']
|
||||||
|
options = mock.MagicMock()
|
||||||
|
session = mock.MagicMock()
|
||||||
|
session.importRPM.return_value = {'build': {'nvr': 'a-draft-build', 'draft': True}}
|
||||||
|
|
||||||
|
nvr = '%(name)s-%(version)s-%(release)s' % self.srpm_header
|
||||||
|
|
||||||
|
# Case 1. import src rpm with --create-draft
|
||||||
|
# result: success
|
||||||
|
expected = "Will create draft build instead if desired nvr doesn't exist\n"
|
||||||
|
expected += "Will create draft build with target nvr: %s while importing\n" % nvr
|
||||||
|
expected += "uploading %s... done\n" % arguments[0]
|
||||||
|
expected += "importing %s... done\n" % arguments[0]
|
||||||
|
expected += "Draft build: a-draft-build created\n"
|
||||||
|
|
||||||
|
self.__do_import_test(
|
||||||
|
options, session, arguments,
|
||||||
|
rpm_header=self.srpm_header,
|
||||||
|
expected=expected,
|
||||||
|
getrpm_called=False,
|
||||||
|
import_opts={'draft': True})
|
||||||
|
|
||||||
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
|
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||||
|
@mock.patch('koji_cli.commands.activate_session')
|
||||||
|
def test_handle_import_binary_rpm_with_create_draft(
|
||||||
|
self,
|
||||||
|
activate_session_mock,
|
||||||
|
stderr,
|
||||||
|
stdout):
|
||||||
|
"""Test handle_import binary RPM import with creating draft build case."""
|
||||||
|
arguments = ['/path/to/bash-4.4.12-5.fc26.rpm', '--create-draft']
|
||||||
|
options = mock.MagicMock()
|
||||||
|
session = mock.MagicMock()
|
||||||
|
|
||||||
|
nvr = '%(name)s-%(version)s-%(release)s' % self.rpm_header
|
||||||
|
|
||||||
|
# Case 1. import bin rpm with --create-draft
|
||||||
|
# result: Aborting import
|
||||||
|
expected = "Will create draft build instead if desired nvr doesn't exist\n"
|
||||||
|
expected += "Missing srpm for draft build creating with target nvr: %s\n" % nvr
|
||||||
|
expected += "Aborting import\n"
|
||||||
|
self.__skip_import_test(
|
||||||
|
options, session, arguments,
|
||||||
|
rpm_header=self.rpm_header,
|
||||||
|
expected=expected,
|
||||||
|
getrpm_called=False)
|
||||||
|
|
||||||
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
|
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||||
|
@mock.patch('koji_cli.commands.activate_session')
|
||||||
|
def test_handle_import_src_bin_rpm_with_create_draft(
|
||||||
|
self,
|
||||||
|
activate_session_mock,
|
||||||
|
stderr,
|
||||||
|
stdout):
|
||||||
|
"""Test handle_import SRPM & RPM import with creating draft build case."""
|
||||||
|
arguments = ['/path/to/bash-4.4.12-5.fc26.rpm', '/path/to/bash-4.4.12-5.fc26.src.rpm',
|
||||||
|
'--create-draft']
|
||||||
|
options = mock.MagicMock()
|
||||||
|
session = mock.MagicMock()
|
||||||
|
session.getRPM.return_value = None
|
||||||
|
session.importRPM.return_value = {'build': {'nvr': 'a-draft-build', 'draft': True}}
|
||||||
|
|
||||||
|
nvr = '%(name)s-%(version)s-%(release)s' % self.srpm_header
|
||||||
|
|
||||||
|
# Case 1. import src & bin rpm with --create-draft
|
||||||
|
# result: success
|
||||||
|
expected = "Will create draft build instead if desired nvr doesn't exist\n"
|
||||||
|
expected += "Will create draft build with target nvr: %s while importing\n" % nvr
|
||||||
|
expected += "uploading %s... done\n" % arguments[1]
|
||||||
|
expected += "importing %s... done\n" % arguments[1]
|
||||||
|
expected += "Draft build: a-draft-build created\n"
|
||||||
|
expected += "uploading %s... done\n" % arguments[0]
|
||||||
|
expected += "importing %s... done\n" % arguments[0]
|
||||||
|
|
||||||
|
self.__do_import_test(
|
||||||
|
options, session, arguments,
|
||||||
|
rpm_headers=[self.rpm_header, self.srpm_header],
|
||||||
|
expected=expected,
|
||||||
|
getrpm_calls=[
|
||||||
|
mock.call(
|
||||||
|
{'name': 'bash', 'version': '4.4.12', 'release': '5.fc26', 'arch': 'x86_64'},
|
||||||
|
build=session.importRPM.return_value['build']
|
||||||
|
)
|
||||||
|
],
|
||||||
|
import_rpm_calls=[
|
||||||
|
mock.call(
|
||||||
|
self.fake_srv_dir, os.path.basename(arguments[1]), draft=True
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
self.fake_srv_dir, os.path.basename(arguments[0]),
|
||||||
|
build=session.importRPM.return_value['build'], draft=True
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
|
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||||
|
@mock.patch('koji_cli.commands.activate_session')
|
||||||
|
def test_handle_import_src_rpm_with_specified_draft_build(
|
||||||
|
self,
|
||||||
|
activate_session_mock,
|
||||||
|
stderr,
|
||||||
|
stdout):
|
||||||
|
"""Test handle_import SRPM import with --draft-build case."""
|
||||||
|
arguments = ['/path/to/bash-4.4.12-5.fc26.rpm', '--draft-build', 'a-draft-build']
|
||||||
|
options = mock.MagicMock()
|
||||||
|
session = mock.MagicMock()
|
||||||
|
build = {
|
||||||
|
'name': 'bash',
|
||||||
|
'version': '4.4.12',
|
||||||
|
'release': '5.fc26#draft_123',
|
||||||
|
'nvr': 'bash-4.4.12-5.fc26',
|
||||||
|
'draft': True,
|
||||||
|
'extra': {
|
||||||
|
'draft': {
|
||||||
|
'target_release': '5.fc26'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'state': self.bstate['COMPLETE']
|
||||||
|
}
|
||||||
|
session.getBuild.return_value = build
|
||||||
|
|
||||||
|
# Case 1. import src rpm with --draft-build
|
||||||
|
# result: success
|
||||||
|
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,
|
||||||
|
getrpm_calls=[mock.call(
|
||||||
|
{'name': 'bash', 'version': '4.4.12', 'release': '5.fc26', 'arch': 'src'},
|
||||||
|
build=build)],
|
||||||
|
import_opts={'build': build, 'draft': True})
|
||||||
|
|
||||||
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
|
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||||
|
@mock.patch('koji_cli.commands.activate_session')
|
||||||
|
def test_handle_import_binary_rpm_with_specified_draft_build(
|
||||||
|
self,
|
||||||
|
activate_session_mock,
|
||||||
|
stderr,
|
||||||
|
stdout):
|
||||||
|
"""Test handle_import RPM import with --draft-build case."""
|
||||||
|
arguments = ['/path/to/bash-4.4.12-5.fc26.rpm', '--draft-build', 'a-draft-build']
|
||||||
|
options = mock.MagicMock()
|
||||||
|
session = mock.MagicMock()
|
||||||
|
session.getRPM.return_value = None
|
||||||
|
build = {
|
||||||
|
'name': 'bash',
|
||||||
|
'version': '4.4.12',
|
||||||
|
'release': '5.fc26#draft_123',
|
||||||
|
'nvr': 'bash-4.4.12-5.fc26',
|
||||||
|
'draft': True,
|
||||||
|
'extra': {
|
||||||
|
'draft': {
|
||||||
|
'target_release': '5.fc26'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'state': self.bstate['COMPLETE']
|
||||||
|
}
|
||||||
|
session.getBuild.return_value = build
|
||||||
|
|
||||||
|
# Case 1. import bin rpm with --draft-build
|
||||||
|
# result: success
|
||||||
|
expected = "uploading %s... done\n" % arguments[0]
|
||||||
|
expected += "importing %s... done\n" % arguments[0]
|
||||||
|
|
||||||
|
self.__do_import_test(
|
||||||
|
options, session, arguments,
|
||||||
|
rpm_header=self.rpm_header,
|
||||||
|
expected=expected,
|
||||||
|
getrpm_calls=[mock.call(
|
||||||
|
{'name': 'bash', 'version': '4.4.12', 'release': '5.fc26', 'arch': 'x86_64'},
|
||||||
|
build=build)],
|
||||||
|
import_opts={'build': build, 'draft': True})
|
||||||
|
|
||||||
|
def test_handle_import_specified_draft_build_invalid(self):
|
||||||
|
"""Test handle_import RPM import with --draft-build case."""
|
||||||
|
arguments = ['/path/to/bash-4.4.12-5.fc26.rpm', '--draft-build', '286']
|
||||||
|
options = mock.MagicMock()
|
||||||
|
session = mock.MagicMock()
|
||||||
|
|
||||||
|
cases = [
|
||||||
|
# build, stderr
|
||||||
|
(None, "No such build: 286"),
|
||||||
|
({'draft': False, 'nvr': 'a-bad-draft'}, "a-bad-draft is not a draft build"),
|
||||||
|
({'draft': True, 'nvr': 'a-bad-draft', 'state': koji.BUILD_STATES['DELETED']},
|
||||||
|
"draft build a-bad-draft is expected as COMPLETE, got DELETED"),
|
||||||
|
({'draft': True, 'nvr': 'a-bad-draft', 'state': koji.BUILD_STATES['COMPLETE'],
|
||||||
|
'extra': {'draft': {'no_target_release': 'omg'}}},
|
||||||
|
"Invalid draft build: a-bad-draft, no draft.target_release found in extra")
|
||||||
|
]
|
||||||
|
for build, expected in cases:
|
||||||
|
session.getBuild.return_value = build
|
||||||
|
# result: error
|
||||||
|
expected += "\n"
|
||||||
|
self.assert_system_exit(
|
||||||
|
handle_import,
|
||||||
|
options,
|
||||||
|
session,
|
||||||
|
arguments,
|
||||||
|
stderr=expected,
|
||||||
|
activate_session=None,
|
||||||
|
exit_code=1)
|
||||||
|
options.reset_mock()
|
||||||
|
session.reset_mock()
|
||||||
|
|
||||||
def test_handle_import_help(self):
|
def test_handle_import_help(self):
|
||||||
"""Test handle_import function help message"""
|
"""Test handle_import function help message"""
|
||||||
self.assert_help(
|
self.assert_help(
|
||||||
|
|
@ -691,6 +932,8 @@ Options:
|
||||||
--create-build Auto-create builds as needed
|
--create-build Auto-create builds as needed
|
||||||
--src-epoch=SRC_EPOCH
|
--src-epoch=SRC_EPOCH
|
||||||
When auto-creating builds, use this epoch
|
When auto-creating builds, use this epoch
|
||||||
|
--create-draft Auto-create draft builds instead as needed
|
||||||
|
--draft-build=NVR|ID The target draft build to import to
|
||||||
""" % self.progname)
|
""" % self.progname)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -609,6 +609,8 @@ Options:
|
||||||
pattern)
|
pattern)
|
||||||
--owner=OWNER List builds built by this owner
|
--owner=OWNER List builds built by this owner
|
||||||
--volume=VOLUME List builds by volume ID
|
--volume=VOLUME List builds by volume ID
|
||||||
|
--draft-only Only list draft builds
|
||||||
|
--no-draft Only list regular builds
|
||||||
-k FIELD, --sort-key=FIELD
|
-k FIELD, --sort-key=FIELD
|
||||||
Sort the list by the named field. Allowed sort keys:
|
Sort the list by the named field. Allowed sort keys:
|
||||||
build_id, owner_name, state
|
build_id, owner_name, state
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,15 @@ class TestCliListTagged(utils.CliTestCase):
|
||||||
'release': '1.el6',
|
'release': '1.el6',
|
||||||
'arch': 'x86_64',
|
'arch': 'x86_64',
|
||||||
'sigkey': 'sigkey',
|
'sigkey': 'sigkey',
|
||||||
|
'extra': None},
|
||||||
|
{'id': 102,
|
||||||
|
'build_id': 2,
|
||||||
|
'name': 'rpmA',
|
||||||
|
'version': '0.0.1',
|
||||||
|
'release': '2.el6',
|
||||||
|
'arch': 'x86_64',
|
||||||
|
'sigkey': 'sigkey',
|
||||||
|
'draft': True,
|
||||||
'extra': None}
|
'extra': None}
|
||||||
], [{'id': 1,
|
], [{'id': 1,
|
||||||
'name': 'packagename',
|
'name': 'packagename',
|
||||||
|
|
@ -50,6 +59,15 @@ class TestCliListTagged(utils.CliTestCase):
|
||||||
'nvr': 'n-v-r',
|
'nvr': 'n-v-r',
|
||||||
'tag_name': 'tag',
|
'tag_name': 'tag',
|
||||||
'owner_name': 'owner',
|
'owner_name': 'owner',
|
||||||
|
'extra': 'extra-value-2'},
|
||||||
|
{'id': 2,
|
||||||
|
'name': 'packagename',
|
||||||
|
'version': 'version',
|
||||||
|
'release': '2.el6#draft_2',
|
||||||
|
'nvr': 'n-v-r',
|
||||||
|
'draft': True,
|
||||||
|
'tag_name': 'tag',
|
||||||
|
'owner_name': 'owner',
|
||||||
'extra': 'extra-value-2'}]]
|
'extra': 'extra-value-2'}]]
|
||||||
self.session.listTagged.return_value = [{'id': 1,
|
self.session.listTagged.return_value = [{'id': 1,
|
||||||
'name': 'packagename',
|
'name': 'packagename',
|
||||||
|
|
@ -77,13 +95,15 @@ Build Tag Built by
|
||||||
---------------------------------------- -------------------- ----------------
|
---------------------------------------- -------------------- ----------------
|
||||||
n-v-r tag owner
|
n-v-r tag owner
|
||||||
"""
|
"""
|
||||||
args = [self.tag, self.pkg, '--latest', '--inherit', '--event', str(self.event_id)]
|
args = [self.tag, self.pkg, '--no-draft', '--latest', '--inherit',
|
||||||
|
'--event', str(self.event_id)]
|
||||||
|
|
||||||
anon_handle_list_tagged(self.options, self.session, args)
|
anon_handle_list_tagged(self.options, self.session, args)
|
||||||
self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
|
self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
|
||||||
self.session.getTag.assert_called_once_with(self.tag, event=self.event_id)
|
self.session.getTag.assert_called_once_with(self.tag, event=self.event_id)
|
||||||
self.session.listTagged.assert_called_once_with(
|
self.session.listTagged.assert_called_once_with(
|
||||||
self.tag, event=self.event_id, inherit=True, latest=True, package=self.pkg)
|
self.tag, event=self.event_id, inherit=True, latest=True, package=self.pkg,
|
||||||
|
draft=2)
|
||||||
self.session.listTaggedRPMS.assert_not_called()
|
self.session.listTaggedRPMS.assert_not_called()
|
||||||
self.assert_console_message(stdout, expected)
|
self.assert_console_message(stdout, expected)
|
||||||
|
|
||||||
|
|
@ -94,14 +114,14 @@ n-v-r tag owner
|
||||||
---------------------------------------- -------------------- ----------------
|
---------------------------------------- -------------------- ----------------
|
||||||
/mnt/koji/packages/packagename/version/1.el6 tag owner
|
/mnt/koji/packages/packagename/version/1.el6 tag owner
|
||||||
"""
|
"""
|
||||||
args = [self.tag, self.pkg, '--latest', '--inherit', '--paths']
|
args = [self.tag, self.pkg, '--latest', '--inherit', '--paths', '--draft-only']
|
||||||
|
|
||||||
anon_handle_list_tagged(self.options, self.session, args)
|
anon_handle_list_tagged(self.options, self.session, args)
|
||||||
self.assert_console_message(stdout, expected)
|
self.assert_console_message(stdout, expected)
|
||||||
self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
|
self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
|
||||||
self.session.getTag.assert_called_once_with(self.tag, event=None)
|
self.session.getTag.assert_called_once_with(self.tag, event=None)
|
||||||
self.session.listTagged.assert_called_once_with(
|
self.session.listTagged.assert_called_once_with(
|
||||||
self.tag, inherit=True, latest=True, package=self.pkg)
|
self.tag, inherit=True, latest=True, package=self.pkg, draft=1)
|
||||||
self.session.listTaggedRPMS.assert_not_called()
|
self.session.listTaggedRPMS.assert_not_called()
|
||||||
|
|
||||||
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
|
|
@ -109,6 +129,7 @@ n-v-r tag owner
|
||||||
def test_list_tagged_rpms(self, event_from_opts_mock, stdout):
|
def test_list_tagged_rpms(self, event_from_opts_mock, stdout):
|
||||||
expected = """sigkey rpmA-0.0.1-1.el6.noarch
|
expected = """sigkey rpmA-0.0.1-1.el6.noarch
|
||||||
sigkey rpmA-0.0.1-1.el6.x86_64
|
sigkey rpmA-0.0.1-1.el6.x86_64
|
||||||
|
sigkey rpmA-0.0.1-2.el6.x86_64 (#draft_2)
|
||||||
"""
|
"""
|
||||||
args = [self.tag, self.pkg, '--latest-n=3', '--rpms', '--sigs',
|
args = [self.tag, self.pkg, '--latest-n=3', '--rpms', '--sigs',
|
||||||
'--arch=x86_64', '--arch=noarch']
|
'--arch=x86_64', '--arch=noarch']
|
||||||
|
|
@ -129,6 +150,7 @@ sigkey rpmA-0.0.1-1.el6.x86_64
|
||||||
def test_list_tagged_rpms_paths(self, event_from_opts_mock, stdout, os_path_exists, isdir):
|
def test_list_tagged_rpms_paths(self, event_from_opts_mock, stdout, os_path_exists, isdir):
|
||||||
expected = """/mnt/koji/packages/packagename/version/1.el6/noarch/rpmA-0.0.1-1.el6.noarch.rpm
|
expected = """/mnt/koji/packages/packagename/version/1.el6/noarch/rpmA-0.0.1-1.el6.noarch.rpm
|
||||||
/mnt/koji/packages/packagename/version/1.el6/x86_64/rpmA-0.0.1-1.el6.x86_64.rpm
|
/mnt/koji/packages/packagename/version/1.el6/x86_64/rpmA-0.0.1-1.el6.x86_64.rpm
|
||||||
|
/mnt/koji/packages/packagename/version/2.el6#draft_2/x86_64/rpmA-0.0.1-2.el6.x86_64.rpm
|
||||||
"""
|
"""
|
||||||
args = [self.tag, self.pkg, '--latest-n=3', '--rpms', '--arch=x86_64', '--paths']
|
args = [self.tag, self.pkg, '--latest-n=3', '--rpms', '--arch=x86_64', '--paths']
|
||||||
|
|
||||||
|
|
@ -233,6 +255,15 @@ n-v-r tag group
|
||||||
self.session.listTaggedRPMS.assert_not_called()
|
self.session.listTaggedRPMS.assert_not_called()
|
||||||
self.session.listTagged.assert_not_called()
|
self.session.listTagged.assert_not_called()
|
||||||
|
|
||||||
|
def test_list_tagged_draft_opts_conflict(self):
|
||||||
|
self.assert_system_exit(
|
||||||
|
anon_handle_list_tagged,
|
||||||
|
self.options, self.session, ['--draft-only', '--no-draft', 'tag', 'pkg1'],
|
||||||
|
stderr=self.format_error_message("--draft-only conflicts with --no-draft"),
|
||||||
|
activate_session=None,
|
||||||
|
exit_code=2)
|
||||||
|
self.ensure_connection_mock.assert_not_called()
|
||||||
|
|
||||||
def test_list_tagged_tag_not_found(self):
|
def test_list_tagged_tag_not_found(self):
|
||||||
self.session.getTag.return_value = None
|
self.session.getTag.return_value = None
|
||||||
self.assert_system_exit(
|
self.assert_system_exit(
|
||||||
|
|
@ -267,4 +298,6 @@ Options:
|
||||||
--event=EVENT# query at event
|
--event=EVENT# query at event
|
||||||
--ts=TIMESTAMP query at last event before timestamp
|
--ts=TIMESTAMP query at last event before timestamp
|
||||||
--repo=REPO# query at event for a repo
|
--repo=REPO# query at event for a repo
|
||||||
|
--draft-only Only list draft builds/rpms
|
||||||
|
--no-draft Only list regular builds/rpms
|
||||||
""" % self.progname)
|
""" % self.progname)
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,17 @@ class TestRpminfo(utils.CliTestCase):
|
||||||
'version': '1.1',
|
'version': '1.1',
|
||||||
'payloadhash': 'b2b95550390e5f213fc25f33822425f7',
|
'payloadhash': 'b2b95550390e5f213fc25f33822425f7',
|
||||||
'size': 7030}
|
'size': 7030}
|
||||||
|
self.listrpminfos = [{'arch': 'src',
|
||||||
|
'build_id': 1,
|
||||||
|
'buildroot_id': 3,
|
||||||
|
'buildtime': 1615877809,
|
||||||
|
'epoch': 7,
|
||||||
|
'id': 290,
|
||||||
|
'name': 'test-rpm',
|
||||||
|
'release': '11',
|
||||||
|
'version': '1.1',
|
||||||
|
'payloadhash': 'b2b95550390e5f213fc25f33822425f7',
|
||||||
|
'size': 7030}]
|
||||||
self.error_format = """Usage: %s rpminfo [options] <n-v-r.a> [<n-v-r.a> ...]
|
self.error_format = """Usage: %s rpminfo [options] <n-v-r.a> [<n-v-r.a> ...]
|
||||||
(Specify the --help global option for a list of other help options)
|
(Specify the --help global option for a list of other help options)
|
||||||
|
|
||||||
|
|
@ -74,9 +85,11 @@ class TestRpminfo(utils.CliTestCase):
|
||||||
self.session.listBuildroots.return_value = [self.buildroot_info]
|
self.session.listBuildroots.return_value = [self.buildroot_info]
|
||||||
self.session.getBuild.return_value = self.buildinfo
|
self.session.getBuild.return_value = self.buildinfo
|
||||||
self.session.getRPM.return_value = self.getrpminfo
|
self.session.getRPM.return_value = self.getrpminfo
|
||||||
|
self.session.listRPMs.return_value = self.listrpminfos
|
||||||
expected_output = """RPM: 7:test-rpm-1.1-11.noarch [294]
|
expected_output = """RPM: 7:test-rpm-1.1-11.noarch [294]
|
||||||
|
Build: test-rpm-1.1-11 [1]
|
||||||
RPM Path: /mnt/koji/packages/test-rpm/1.1/11/noarch/test-rpm-1.1-11.noarch.rpm
|
RPM Path: /mnt/koji/packages/test-rpm/1.1/11/noarch/test-rpm-1.1-11.noarch.rpm
|
||||||
SRPM: 7:test-rpm-1.1-11 [1]
|
SRPM: 7:test-rpm-1.1-11 [290]
|
||||||
SRPM Path: /mnt/koji/packages/test-rpm/1.1/11/src/test-rpm-1.1-11.src.rpm
|
SRPM Path: /mnt/koji/packages/test-rpm/1.1/11/src/test-rpm-1.1-11.src.rpm
|
||||||
Built: Tue, 16 Mar 2021 06:56:49 UTC
|
Built: Tue, 16 Mar 2021 06:56:49 UTC
|
||||||
SIGMD5: b2b95550390e5f213fc25f33822425f7
|
SIGMD5: b2b95550390e5f213fc25f33822425f7
|
||||||
|
|
@ -98,6 +111,8 @@ Used in 1 buildroots:
|
||||||
rpmID=self.getrpminfo['id'])
|
rpmID=self.getrpminfo['id'])
|
||||||
self.session.getBuild.assert_called_once_with(self.getrpminfo['build_id'])
|
self.session.getBuild.assert_called_once_with(self.getrpminfo['build_id'])
|
||||||
self.session.getRPM.assert_called_once_with(rpm_nvra)
|
self.session.getRPM.assert_called_once_with(rpm_nvra)
|
||||||
|
self.session.listRPMs.assert_called_once_with(buildID=self.getrpminfo['build_id'],
|
||||||
|
arches='src')
|
||||||
|
|
||||||
def test_handle_rpminfo_non_exist_nvra(self):
|
def test_handle_rpminfo_non_exist_nvra(self):
|
||||||
rpm_nvra = 'test-rpm-nvra.arch'
|
rpm_nvra = 'test-rpm-nvra.arch'
|
||||||
|
|
@ -119,9 +134,11 @@ Used in 1 buildroots:
|
||||||
self.session.listBuildroots.return_value = [self.buildroot_info]
|
self.session.listBuildroots.return_value = [self.buildroot_info]
|
||||||
self.session.getBuild.return_value = self.buildinfo
|
self.session.getBuild.return_value = self.buildinfo
|
||||||
self.session.getRPM.side_effect = [None, self.getrpminfo]
|
self.session.getRPM.side_effect = [None, self.getrpminfo]
|
||||||
|
self.session.listRPMs.return_value = self.listrpminfos
|
||||||
expected_output = """RPM: 7:test-rpm-1.1-11.noarch [294]
|
expected_output = """RPM: 7:test-rpm-1.1-11.noarch [294]
|
||||||
|
Build: test-rpm-1.1-11 [1]
|
||||||
RPM Path: /mnt/koji/packages/test-rpm/1.1/11/noarch/test-rpm-1.1-11.noarch.rpm
|
RPM Path: /mnt/koji/packages/test-rpm/1.1/11/noarch/test-rpm-1.1-11.noarch.rpm
|
||||||
SRPM: 7:test-rpm-1.1-11 [1]
|
SRPM: 7:test-rpm-1.1-11 [290]
|
||||||
SRPM Path: /mnt/koji/packages/test-rpm/1.1/11/src/test-rpm-1.1-11.src.rpm
|
SRPM Path: /mnt/koji/packages/test-rpm/1.1/11/src/test-rpm-1.1-11.src.rpm
|
||||||
Built: Tue, 16 Mar 2021 06:56:49 UTC
|
Built: Tue, 16 Mar 2021 06:56:49 UTC
|
||||||
SIGMD5: b2b95550390e5f213fc25f33822425f7
|
SIGMD5: b2b95550390e5f213fc25f33822425f7
|
||||||
|
|
@ -150,6 +167,45 @@ Used in 1 buildroots:
|
||||||
rpmID=self.getrpminfo['id'])
|
rpmID=self.getrpminfo['id'])
|
||||||
self.session.getBuild.assert_called_once_with(self.getrpminfo['build_id'])
|
self.session.getBuild.assert_called_once_with(self.getrpminfo['build_id'])
|
||||||
self.assertEqual(self.session.getRPM.call_count, 2)
|
self.assertEqual(self.session.getRPM.call_count, 2)
|
||||||
|
self.session.listRPMs.assert_called_once_with(buildID=self.getrpminfo['build_id'],
|
||||||
|
arches='src')
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('sys.stdout', new_callable=StringIO)
|
||||||
|
def test_handle_rpminfo_with_build(self, stdout):
|
||||||
|
rpm_nvra = 'test-rpm-1.1-11.noarch'
|
||||||
|
self.session.getBuildroot.return_value = self.buildroot_info
|
||||||
|
self.session.listBuildroots.return_value = [self.buildroot_info]
|
||||||
|
self.session.getBuild.return_value = self.buildinfo
|
||||||
|
self.session.getRPM.return_value = self.getrpminfo
|
||||||
|
self.session.listRPMs.return_value = self.listrpminfos
|
||||||
|
expected_output = """RPM: 7:test-rpm-1.1-11.noarch [294]
|
||||||
|
Build: test-rpm-1.1-11 [1]
|
||||||
|
RPM Path: /mnt/koji/packages/test-rpm/1.1/11/noarch/test-rpm-1.1-11.noarch.rpm
|
||||||
|
SRPM: 7:test-rpm-1.1-11 [290]
|
||||||
|
SRPM Path: /mnt/koji/packages/test-rpm/1.1/11/src/test-rpm-1.1-11.src.rpm
|
||||||
|
Built: Tue, 16 Mar 2021 06:56:49 UTC
|
||||||
|
SIGMD5: b2b95550390e5f213fc25f33822425f7
|
||||||
|
Size: 7030
|
||||||
|
Build ID: 1
|
||||||
|
Buildroot: 3 (tag test-tag, arch x86_64, repo 2)
|
||||||
|
Build Host: kojibuilder
|
||||||
|
Build Task: 10
|
||||||
|
Used in 1 buildroots:
|
||||||
|
id build tag arch build host
|
||||||
|
-------- ---------------------------- -------- -----------------------------
|
||||||
|
3 test-tag x86_64 kojibuilder
|
||||||
|
"""
|
||||||
|
|
||||||
|
anon_handle_rpminfo(self.options, self.session, ['--buildroot', '--build', 'any', rpm_nvra])
|
||||||
|
self.assert_console_message(stdout, expected_output)
|
||||||
|
self.session.getBuildroot.assert_called_once_with(self.getrpminfo['buildroot_id'])
|
||||||
|
self.session.listBuildroots.assert_called_once_with(queryOpts={'order': 'buildroot.id'},
|
||||||
|
rpmID=self.getrpminfo['id'])
|
||||||
|
self.session.getBuild.assert_called_once_with(self.getrpminfo['build_id'])
|
||||||
|
self.session.getRPM.assert_called_once_with(rpm_nvra, build='any')
|
||||||
|
self.session.listRPMs.assert_called_once_with(buildID=self.getrpminfo['build_id'],
|
||||||
|
arches='src')
|
||||||
|
|
||||||
def test_rpminfo_without_option(self):
|
def test_rpminfo_without_option(self):
|
||||||
arguments = []
|
arguments = []
|
||||||
|
|
@ -171,6 +227,7 @@ Used in 1 buildroots:
|
||||||
(Specify the --help global option for a list of other help options)
|
(Specify the --help global option for a list of other help options)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--buildroots show buildroots the rpm was used in
|
--buildroots show buildroots the rpm was used in
|
||||||
|
--build=NVR|ID show the rpm(s) in the build
|
||||||
""" % self.progname)
|
""" % self.progname)
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ Options:
|
||||||
--wait Wait on build, even if running in the background
|
--wait Wait on build, even if running in the background
|
||||||
--nowait Don't wait on build
|
--nowait Don't wait on build
|
||||||
--background Run the build at a lower priority
|
--background Run the build at a lower priority
|
||||||
|
--draft Build draft build instead
|
||||||
""" % self.progname)
|
""" % self.progname)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ class TestGetRPM(DBQueryTestCase):
|
||||||
self.exports = kojihub.RootExports()
|
self.exports = kojihub.RootExports()
|
||||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||||
self.get_external_repo_id = mock.patch('kojihub.kojihub.get_external_repo_id').start()
|
self.get_external_repo_id = mock.patch('kojihub.kojihub.get_external_repo_id').start()
|
||||||
|
self.find_build_id = mock.patch('kojihub.kojihub.find_build_id').start()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
mock.patch.stopall()
|
||||||
|
|
||||||
def test_wrong_type_rpminfo(self):
|
def test_wrong_type_rpminfo(self):
|
||||||
rpminfo = ['test-user']
|
rpminfo = ['test-user']
|
||||||
|
|
@ -31,11 +35,10 @@ class TestGetRPM(DBQueryTestCase):
|
||||||
|
|
||||||
self.assertEqual(len(self.queries), 1)
|
self.assertEqual(len(self.queries), 1)
|
||||||
query = self.queries[0]
|
query = self.queries[0]
|
||||||
str(query)
|
|
||||||
self.assertEqual(query.tables, ['rpminfo'])
|
self.assertEqual(query.tables, ['rpminfo'])
|
||||||
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
||||||
'epoch', 'arch', 'external_repo_id', 'external_repo.name', 'payloadhash',
|
'epoch', 'arch', 'draft', 'external_repo_id', 'external_repo.name',
|
||||||
'size', 'buildtime', 'metadata_only', 'extra']
|
'payloadhash', 'size', 'buildtime', 'metadata_only', 'extra']
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
self.assertEqual(query.clauses, ['external_repo_id = 0', "rpminfo.id=%(id)s"])
|
self.assertEqual(query.clauses, ['external_repo_id = 0', "rpminfo.id=%(id)s"])
|
||||||
self.assertEqual(query.joins,
|
self.assertEqual(query.joins,
|
||||||
|
|
@ -50,11 +53,10 @@ class TestGetRPM(DBQueryTestCase):
|
||||||
|
|
||||||
self.assertEqual(len(self.queries), 1)
|
self.assertEqual(len(self.queries), 1)
|
||||||
query = self.queries[0]
|
query = self.queries[0]
|
||||||
str(query)
|
|
||||||
self.assertEqual(query.tables, ['rpminfo'])
|
self.assertEqual(query.tables, ['rpminfo'])
|
||||||
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
||||||
'epoch', 'arch', 'external_repo_id', 'external_repo.name', 'payloadhash',
|
'epoch', 'arch', 'draft', 'external_repo_id', 'external_repo.name',
|
||||||
'size', 'buildtime', 'metadata_only', 'extra']
|
'payloadhash', 'size', 'buildtime', 'metadata_only', 'extra']
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
self.assertEqual(query.clauses, ["rpminfo.id=%(id)s"])
|
self.assertEqual(query.clauses, ["rpminfo.id=%(id)s"])
|
||||||
self.assertEqual(query.joins,
|
self.assertEqual(query.joins,
|
||||||
|
|
@ -70,11 +72,10 @@ class TestGetRPM(DBQueryTestCase):
|
||||||
|
|
||||||
self.assertEqual(len(self.queries), 1)
|
self.assertEqual(len(self.queries), 1)
|
||||||
query = self.queries[0]
|
query = self.queries[0]
|
||||||
str(query)
|
|
||||||
self.assertEqual(query.tables, ['rpminfo'])
|
self.assertEqual(query.tables, ['rpminfo'])
|
||||||
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
||||||
'epoch', 'arch', 'external_repo_id', 'external_repo.name', 'payloadhash',
|
'epoch', 'arch', 'draft', 'external_repo_id', 'external_repo.name',
|
||||||
'size', 'buildtime', 'metadata_only', 'extra']
|
'payloadhash', 'size', 'buildtime', 'metadata_only', 'extra']
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
self.assertEqual(query.clauses, ["rpminfo.id=%(id)s"])
|
self.assertEqual(query.clauses, ["rpminfo.id=%(id)s"])
|
||||||
self.assertEqual(query.joins,
|
self.assertEqual(query.joins,
|
||||||
|
|
@ -87,11 +88,10 @@ class TestGetRPM(DBQueryTestCase):
|
||||||
|
|
||||||
self.assertEqual(len(self.queries), 1)
|
self.assertEqual(len(self.queries), 1)
|
||||||
query = self.queries[0]
|
query = self.queries[0]
|
||||||
str(query)
|
|
||||||
self.assertEqual(query.tables, ['rpminfo'])
|
self.assertEqual(query.tables, ['rpminfo'])
|
||||||
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
||||||
'epoch', 'arch', 'external_repo_id', 'external_repo.name', 'payloadhash',
|
'epoch', 'arch', 'draft', 'external_repo_id', 'external_repo.name',
|
||||||
'size', 'buildtime', 'metadata_only', 'extra']
|
'payloadhash', 'size', 'buildtime', 'metadata_only', 'extra']
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
self.assertEqual(query.clauses, ["rpminfo.name=%(name)s AND version=%(version)s "
|
self.assertEqual(query.clauses, ["rpminfo.name=%(name)s AND version=%(version)s "
|
||||||
"AND release=%(release)s AND arch=%(arch)s"])
|
"AND release=%(release)s AND arch=%(arch)s"])
|
||||||
|
|
@ -110,17 +110,36 @@ class TestGetRPM(DBQueryTestCase):
|
||||||
|
|
||||||
self.assertEqual(len(self.queries), 1)
|
self.assertEqual(len(self.queries), 1)
|
||||||
query = self.queries[0]
|
query = self.queries[0]
|
||||||
str(query)
|
|
||||||
self.assertEqual(query.tables, ['rpminfo'])
|
self.assertEqual(query.tables, ['rpminfo'])
|
||||||
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
||||||
'epoch', 'arch', 'external_repo_id', 'external_repo.name', 'payloadhash',
|
'epoch', 'arch', 'draft', 'external_repo_id', 'external_repo.name',
|
||||||
'size', 'buildtime', 'metadata_only', 'extra']
|
'payloadhash', 'size', 'buildtime', 'metadata_only', 'extra']
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
self.assertEqual(query.clauses,
|
self.assertEqual(query.clauses,
|
||||||
["external_repo_id = %(external_repo_id)i", "rpminfo.id=%(id)s"])
|
["external_repo_id = %(external_repo_id)i", "rpminfo.id=%(id)s"])
|
||||||
self.assertEqual(query.joins,
|
self.assertEqual(query.joins,
|
||||||
['external_repo ON rpminfo.external_repo_id = external_repo.id'])
|
['external_repo ON rpminfo.external_repo_id = external_repo.id'])
|
||||||
self.assertEqual(query.values, rpminfo_data)
|
self.assertEqual(query.values, rpminfo_data)
|
||||||
|
|
||||||
|
def test_rpm_info_with_build(self):
|
||||||
|
rpminfo = {'id': 123, 'name': 'testrpm-1.23-4.x86_64.rpm', 'build_id': 101}
|
||||||
|
self.find_build_id.return_value = 101
|
||||||
|
rpminfo_data = rpminfo.copy()
|
||||||
|
|
||||||
|
kojihub.get_rpm(rpminfo, multi=True, build='any')
|
||||||
|
|
||||||
|
self.assertEqual(len(self.queries), 1)
|
||||||
|
query = self.queries[0]
|
||||||
|
self.assertEqual(query.tables, ['rpminfo'])
|
||||||
|
columns = ['rpminfo.id', 'build_id', 'buildroot_id', 'rpminfo.name', 'version', 'release',
|
||||||
|
'epoch', 'arch', 'draft', 'external_repo_id', 'external_repo.name',
|
||||||
|
'payloadhash', 'size', 'buildtime', 'metadata_only', 'extra']
|
||||||
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
|
self.assertEqual(query.clauses,
|
||||||
|
["rpminfo.build_id = %(build_id)s", "rpminfo.id=%(id)s"])
|
||||||
|
self.assertEqual(query.joins,
|
||||||
|
['external_repo ON rpminfo.external_repo_id = external_repo.id'])
|
||||||
|
self.assertEqual(query.values, rpminfo_data)
|
||||||
|
|
||||||
|
|
||||||
class TestGetRPMHeaders(unittest.TestCase):
|
class TestGetRPMHeaders(unittest.TestCase):
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ class TestGetBuild(DBQueryTestCase):
|
||||||
self.assertEqual(query.columns,
|
self.assertEqual(query.columns,
|
||||||
['build.id', 'build.cg_id', 'build.completion_time',
|
['build.id', 'build.cg_id', 'build.completion_time',
|
||||||
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
||||||
"date_part('epoch', events.time)", 'build.epoch', 'build.extra',
|
"date_part('epoch', events.time)", 'build.draft', 'build.epoch',
|
||||||
'build.id', 'package.name',
|
'build.extra', 'build.id', 'package.name',
|
||||||
"package.name || '-' || build.version || '-' || build.release",
|
"package.name || '-' || build.version || '-' || build.release",
|
||||||
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
||||||
'build.source', 'build.start_time',
|
'build.source', 'build.start_time',
|
||||||
|
|
@ -64,8 +64,8 @@ class TestGetBuild(DBQueryTestCase):
|
||||||
self.assertEqual(query.columns,
|
self.assertEqual(query.columns,
|
||||||
['build.id', 'build.cg_id', 'build.completion_time',
|
['build.id', 'build.cg_id', 'build.completion_time',
|
||||||
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
||||||
"date_part('epoch', events.time)", 'build.epoch', 'build.extra',
|
"date_part('epoch', events.time)", 'build.draft', 'build.epoch',
|
||||||
'build.id', 'package.name',
|
'build.extra', 'build.id', 'package.name',
|
||||||
"package.name || '-' || build.version || '-' || build.release",
|
"package.name || '-' || build.version || '-' || build.release",
|
||||||
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
||||||
'build.source', 'build.start_time',
|
'build.source', 'build.start_time',
|
||||||
|
|
@ -111,8 +111,8 @@ class TestGetBuild(DBQueryTestCase):
|
||||||
self.assertEqual(query.columns,
|
self.assertEqual(query.columns,
|
||||||
['build.id', 'build.cg_id', 'build.completion_time',
|
['build.id', 'build.cg_id', 'build.completion_time',
|
||||||
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
||||||
"date_part('epoch', events.time)", 'build.epoch', 'build.extra',
|
"date_part('epoch', events.time)", 'build.draft', 'build.epoch',
|
||||||
'build.id', 'package.name',
|
'build.extra', 'build.id', 'package.name',
|
||||||
"package.name || '-' || build.version || '-' || build.release",
|
"package.name || '-' || build.version || '-' || build.release",
|
||||||
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
||||||
'build.source', 'build.start_time',
|
'build.source', 'build.start_time',
|
||||||
|
|
@ -138,8 +138,8 @@ class TestGetBuild(DBQueryTestCase):
|
||||||
self.assertEqual(query.columns,
|
self.assertEqual(query.columns,
|
||||||
['build.id', 'build.cg_id', 'build.completion_time',
|
['build.id', 'build.cg_id', 'build.completion_time',
|
||||||
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
"date_part('epoch', build.completion_time)", 'events.id', 'events.time',
|
||||||
"date_part('epoch', events.time)", 'build.epoch', 'build.extra',
|
"date_part('epoch', events.time)", 'build.draft', 'build.epoch',
|
||||||
'build.id', 'package.name',
|
'build.extra', 'build.id', 'package.name',
|
||||||
"package.name || '-' || build.version || '-' || build.release",
|
"package.name || '-' || build.version || '-' || build.release",
|
||||||
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
'users.id', 'users.name', 'package.id', 'package.name', 'build.release',
|
||||||
'build.source', 'build.start_time',
|
'build.source', 'build.start_time',
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ class TestGetNextRelease(DBQueryTestCase):
|
||||||
self.assertEqual(query.tables, ['build'])
|
self.assertEqual(query.tables, ['build'])
|
||||||
self.assertEqual(query.joins, ['package ON build.pkg_id = package.id'])
|
self.assertEqual(query.joins, ['package ON build.pkg_id = package.id'])
|
||||||
self.assertEqual(query.clauses,
|
self.assertEqual(query.clauses,
|
||||||
['name = %(name)s', 'state in %(states)s', 'version = %(version)s'])
|
['NOT draft', 'name = %(name)s', 'state in %(states)s',
|
||||||
|
'version = %(version)s'])
|
||||||
self.assertEqual(query.values, {'name': self.binfo['name'],
|
self.assertEqual(query.values, {'name': self.binfo['name'],
|
||||||
'version': self.binfo['version'],
|
'version': self.binfo['version'],
|
||||||
'states': (1, 2, 0)
|
'states': (1, 2, 0)
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ class TestImportBuild(unittest.TestCase):
|
||||||
|
|
||||||
fields = [
|
fields = [
|
||||||
'completion_time',
|
'completion_time',
|
||||||
|
'draft',
|
||||||
'epoch',
|
'epoch',
|
||||||
'extra',
|
'extra',
|
||||||
'id',
|
'id',
|
||||||
|
|
@ -123,6 +124,7 @@ class TestImportBuild(unittest.TestCase):
|
||||||
'release': 'release',
|
'release': 'release',
|
||||||
'pkg_id': mock.ANY,
|
'pkg_id': mock.ANY,
|
||||||
'id': mock.ANY,
|
'id': mock.ANY,
|
||||||
|
'draft': False
|
||||||
}
|
}
|
||||||
self._dml.assert_called_once_with(statement, values)
|
self._dml.assert_called_once_with(statement, values)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,12 @@ class TestImportRPM(unittest.TestCase):
|
||||||
1003: 'epoch',
|
1003: 'epoch',
|
||||||
1006: 'buildtime',
|
1006: 'buildtime',
|
||||||
1022: 'arch',
|
1022: 'arch',
|
||||||
1044: 'name-version-release.arch',
|
1044: 'name-version-release.src.rpm',
|
||||||
1106: 'sourcepackage',
|
1106: 'sourcepackage',
|
||||||
261: 'payload hash',
|
261: 'payload hash',
|
||||||
}
|
}
|
||||||
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
||||||
|
self.new_build = mock.patch('kojihub.kojihub.new_build').start()
|
||||||
self.get_rpm_header = mock.patch('koji.get_rpm_header').start()
|
self.get_rpm_header = mock.patch('koji.get_rpm_header').start()
|
||||||
self.new_typed_build = mock.patch('kojihub.kojihub.new_typed_build').start()
|
self.new_typed_build = mock.patch('kojihub.kojihub.new_typed_build').start()
|
||||||
self.nextval = mock.patch('kojihub.kojihub.nextval').start()
|
self.nextval = mock.patch('kojihub.kojihub.nextval').start()
|
||||||
|
|
@ -65,6 +66,7 @@ class TestImportRPM(unittest.TestCase):
|
||||||
kojihub.import_rpm("this does not exist")
|
kojihub.import_rpm("this does not exist")
|
||||||
|
|
||||||
def test_import_rpm_failed_build(self):
|
def test_import_rpm_failed_build(self):
|
||||||
|
self.os_path_basename.return_value = 'name-version-release.arch.rpm'
|
||||||
self.get_rpm_header.return_value = self.rpm_header_retval
|
self.get_rpm_header.return_value = self.rpm_header_retval
|
||||||
self.get_build.return_value = {
|
self.get_build.return_value = {
|
||||||
'state': koji.BUILD_STATES['FAILED'],
|
'state': koji.BUILD_STATES['FAILED'],
|
||||||
|
|
@ -72,9 +74,11 @@ class TestImportRPM(unittest.TestCase):
|
||||||
'version': 'version',
|
'version': 'version',
|
||||||
'release': 'release',
|
'release': 'release',
|
||||||
}
|
}
|
||||||
with self.assertRaises(koji.GenericError):
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
kojihub.import_rpm(self.filename)
|
kojihub.import_rpm(self.filename)
|
||||||
|
self.assertEqual("Build is FAILED: name-version-release", str(cm.exception))
|
||||||
self.assertEqual(len(self.inserts), 0)
|
self.assertEqual(len(self.inserts), 0)
|
||||||
|
|
||||||
|
|
||||||
def test_import_rpm_completed_build(self):
|
def test_import_rpm_completed_build(self):
|
||||||
self.os_path_basename.return_value = 'name-version-release.arch.rpm'
|
self.os_path_basename.return_value = 'name-version-release.arch.rpm'
|
||||||
|
|
@ -94,6 +98,7 @@ class TestImportRPM(unittest.TestCase):
|
||||||
'name': 'name',
|
'name': 'name',
|
||||||
'arch': 'arch',
|
'arch': 'arch',
|
||||||
'buildtime': 'buildtime',
|
'buildtime': 'buildtime',
|
||||||
|
'draft': False,
|
||||||
'payloadhash': '7061796c6f61642068617368',
|
'payloadhash': '7061796c6f61642068617368',
|
||||||
'epoch': 'epoch',
|
'epoch': 'epoch',
|
||||||
'version': 'version',
|
'version': 'version',
|
||||||
|
|
@ -114,7 +119,7 @@ class TestImportRPM(unittest.TestCase):
|
||||||
retval = copy.copy(self.rpm_header_retval)
|
retval = copy.copy(self.rpm_header_retval)
|
||||||
retval.update({
|
retval.update({
|
||||||
'filename': 'name-version-release.arch.rpm',
|
'filename': 'name-version-release.arch.rpm',
|
||||||
1044: 'name-version-release.src',
|
1044: 'name-version-release.src.rpm.bad',
|
||||||
1022: 'src',
|
1022: 'src',
|
||||||
1106: 1,
|
1106: 1,
|
||||||
})
|
})
|
||||||
|
|
@ -133,6 +138,7 @@ class TestImportRPM(unittest.TestCase):
|
||||||
'name': 'name',
|
'name': 'name',
|
||||||
'arch': 'src',
|
'arch': 'src',
|
||||||
'buildtime': 'buildtime',
|
'buildtime': 'buildtime',
|
||||||
|
'draft': False,
|
||||||
'payloadhash': '7061796c6f61642068617368',
|
'payloadhash': '7061796c6f61642068617368',
|
||||||
'epoch': 'epoch',
|
'epoch': 'epoch',
|
||||||
'version': 'version',
|
'version': 'version',
|
||||||
|
|
@ -149,10 +155,9 @@ class TestImportRPM(unittest.TestCase):
|
||||||
self.assertEqual(insert.rawdata, {})
|
self.assertEqual(insert.rawdata, {})
|
||||||
|
|
||||||
def test_non_exist_file(self):
|
def test_non_exist_file(self):
|
||||||
basename = 'rpm-1-34'
|
|
||||||
self.os_path_exists.return_value = False
|
self.os_path_exists.return_value = False
|
||||||
with self.assertRaises(koji.GenericError) as cm:
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
kojihub.import_rpm(self.filename, basename)
|
kojihub.import_rpm(self.filename)
|
||||||
self.assertEqual(f"No such file: {self.filename}", str(cm.exception))
|
self.assertEqual(f"No such file: {self.filename}", str(cm.exception))
|
||||||
self.assertEqual(len(self.inserts), 0)
|
self.assertEqual(len(self.inserts), 0)
|
||||||
|
|
||||||
|
|
@ -172,3 +177,211 @@ class TestImportRPM(unittest.TestCase):
|
||||||
kojihub.import_rpm(self.src_filename)
|
kojihub.import_rpm(self.src_filename)
|
||||||
self.assertEqual("No such build", str(cm.exception))
|
self.assertEqual("No such build", str(cm.exception))
|
||||||
self.assertEqual(len(self.inserts), 0)
|
self.assertEqual(len(self.inserts), 0)
|
||||||
|
|
||||||
|
def test_import_draft_rpm_completed_build(self):
|
||||||
|
self.os_path_basename.return_value = 'name-version-release.arch.rpm'
|
||||||
|
self.get_rpm_header.return_value = self.rpm_header_retval
|
||||||
|
self.get_build.return_value = {
|
||||||
|
'state': koji.BUILD_STATES['COMPLETE'],
|
||||||
|
'name': 'name',
|
||||||
|
'version': 'version',
|
||||||
|
'release': 'release',
|
||||||
|
'id': 12345,
|
||||||
|
}
|
||||||
|
self.nextval.return_value = 9876
|
||||||
|
kojihub.import_rpm(self.filename)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'build_id': 12345,
|
||||||
|
'name': 'name',
|
||||||
|
'arch': 'arch',
|
||||||
|
'buildtime': 'buildtime',
|
||||||
|
'draft': False,
|
||||||
|
'payloadhash': '7061796c6f61642068617368',
|
||||||
|
'epoch': 'epoch',
|
||||||
|
'version': 'version',
|
||||||
|
'buildroot_id': None,
|
||||||
|
'release': 'release',
|
||||||
|
'external_repo_id': 0,
|
||||||
|
'id': 9876,
|
||||||
|
'size': 0,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(self.inserts), 1)
|
||||||
|
insert = self.inserts[0]
|
||||||
|
self.assertEqual(insert.table, 'rpminfo')
|
||||||
|
self.assertEqual(insert.data, data)
|
||||||
|
self.assertEqual(insert.rawdata, {})
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_draft_conflict(self):
|
||||||
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
|
kojihub.import_rpm(self.filename, buildinfo={'id': 1024, 'draft': False}, draft=True)
|
||||||
|
self.assertEqual("draft property: False of build: 1024 mismatch, True is expected",
|
||||||
|
str(cm.exception))
|
||||||
|
self.assertEqual(len(self.inserts), 0)
|
||||||
|
|
||||||
|
def test_import_draft_rpm_without_buildinfo(self):
|
||||||
|
self.os_path_basename.return_value = 'name-version-release.arch.rpm'
|
||||||
|
self.get_rpm_header.return_value = self.rpm_header_retval
|
||||||
|
|
||||||
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
|
kojihub.import_rpm(self.filename, draft=True)
|
||||||
|
self.assertEqual(f"Cannot import draft rpm: {self.os_path_basename.return_value}"
|
||||||
|
" without specifying a build", str(cm.exception))
|
||||||
|
self.assertEqual(len(self.inserts), 0)
|
||||||
|
|
||||||
|
def test_import_draft_rpm_non_extra_target_release(self):
|
||||||
|
self.os_path_basename.return_value = 'name-version-release.arch.rpm'
|
||||||
|
self.get_rpm_header.return_value = self.rpm_header_retval
|
||||||
|
|
||||||
|
buildinfo = {
|
||||||
|
'state': koji.BUILD_STATES['DELETED'],
|
||||||
|
'name': 'name',
|
||||||
|
'version': 'version',
|
||||||
|
'release': 'release',
|
||||||
|
'id': 12345,
|
||||||
|
'draft': True
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
|
kojihub.import_rpm(self.filename, buildinfo=buildinfo, draft=True)
|
||||||
|
self.assertEqual(
|
||||||
|
f'target release of draft build not found in extra of build: {buildinfo}',
|
||||||
|
str(cm.exception)
|
||||||
|
)
|
||||||
|
self.assertEqual(len(self.inserts), 0)
|
||||||
|
|
||||||
|
def test_import_draft_rpm_valid(self):
|
||||||
|
self.os_path_basename.return_value = 'name-version-release.arch.rpm'
|
||||||
|
self.get_rpm_header.return_value = self.rpm_header_retval
|
||||||
|
|
||||||
|
buildinfo = {
|
||||||
|
'state': koji.BUILD_STATES['COMPLETE'],
|
||||||
|
'name': 'name',
|
||||||
|
'version': 'version',
|
||||||
|
'release': 'release',
|
||||||
|
'id': 12345,
|
||||||
|
'draft': True,
|
||||||
|
'extra': {
|
||||||
|
'draft': {
|
||||||
|
'target_release': 'release'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.nextval.return_value = 9876
|
||||||
|
kojihub.import_rpm(self.filename, buildinfo=buildinfo, draft=True)
|
||||||
|
data = {
|
||||||
|
'build_id': 12345,
|
||||||
|
'name': 'name',
|
||||||
|
'arch': 'arch',
|
||||||
|
'buildtime': 'buildtime',
|
||||||
|
'draft': True,
|
||||||
|
'payloadhash': '7061796c6f61642068617368',
|
||||||
|
'epoch': 'epoch',
|
||||||
|
'version': 'version',
|
||||||
|
'buildroot_id': None,
|
||||||
|
'release': 'release',
|
||||||
|
'external_repo_id': 0,
|
||||||
|
'id': 9876,
|
||||||
|
'size': 0,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(self.inserts), 1)
|
||||||
|
insert = self.inserts[0]
|
||||||
|
self.assertEqual(insert.table, 'rpminfo')
|
||||||
|
self.assertEqual(insert.data, data)
|
||||||
|
self.assertEqual(insert.rawdata, {})
|
||||||
|
|
||||||
|
def test_import_draft_srpm_with_buildinfo(self):
|
||||||
|
self.os_path_basename.return_value = 'name-version-release.src.rpm'
|
||||||
|
retval = copy.copy(self.rpm_header_retval)
|
||||||
|
retval.update({
|
||||||
|
'filename': 'name-version-release.src.rpm',
|
||||||
|
1044: 'name-version-release.src.rpm.bad',
|
||||||
|
1022: 'src',
|
||||||
|
1106: 1,
|
||||||
|
})
|
||||||
|
self.get_rpm_header.return_value = retval
|
||||||
|
buildinfo = {
|
||||||
|
'state': koji.BUILD_STATES['COMPLETE'],
|
||||||
|
'name': 'name',
|
||||||
|
'version': 'version',
|
||||||
|
'release': 'release',
|
||||||
|
'id': 12345,
|
||||||
|
'draft': True,
|
||||||
|
'extra': {
|
||||||
|
'draft': {
|
||||||
|
'target_release': 'release'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.nextval.return_value = 9876
|
||||||
|
kojihub.import_rpm(self.src_filename, buildinfo=buildinfo, draft=True)
|
||||||
|
data = {
|
||||||
|
'build_id': 12345,
|
||||||
|
'name': 'name',
|
||||||
|
'arch': 'src',
|
||||||
|
'buildtime': 'buildtime',
|
||||||
|
'draft': True,
|
||||||
|
'payloadhash': '7061796c6f61642068617368',
|
||||||
|
'epoch': 'epoch',
|
||||||
|
'version': 'version',
|
||||||
|
'buildroot_id': None,
|
||||||
|
'release': 'release',
|
||||||
|
'external_repo_id': 0,
|
||||||
|
'id': 9876,
|
||||||
|
'size': 0,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(self.inserts), 1)
|
||||||
|
insert = self.inserts[0]
|
||||||
|
self.assertEqual(insert.table, 'rpminfo')
|
||||||
|
self.assertEqual(insert.data, data)
|
||||||
|
self.assertEqual(insert.rawdata, {})
|
||||||
|
|
||||||
|
def test_import_draft_srpm_without_buildinfo(self):
|
||||||
|
self.os_path_basename.return_value = 'name-version-release.src.rpm'
|
||||||
|
retval = copy.copy(self.rpm_header_retval)
|
||||||
|
retval.update({
|
||||||
|
'filename': 'name-version-release.src.rpm',
|
||||||
|
1044: 'name-version-release.src.rpm.bad',
|
||||||
|
1022: 'src',
|
||||||
|
1106: 1,
|
||||||
|
})
|
||||||
|
self.get_rpm_header.return_value = retval
|
||||||
|
self.get_build.return_value = {
|
||||||
|
'state': koji.BUILD_STATES['COMPLETE'],
|
||||||
|
'name': 'name',
|
||||||
|
'version': 'version',
|
||||||
|
'release': 'release',
|
||||||
|
'id': 5566,
|
||||||
|
'draft': True,
|
||||||
|
'extra': {
|
||||||
|
'draft': {
|
||||||
|
'target_release': 'release'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.new_build.return_value = 5566
|
||||||
|
self.nextval.return_value = 9876
|
||||||
|
kojihub.import_rpm(self.src_filename, draft=True)
|
||||||
|
data = {
|
||||||
|
'build_id': 5566,
|
||||||
|
'name': 'name',
|
||||||
|
'arch': 'src',
|
||||||
|
'buildtime': 'buildtime',
|
||||||
|
'draft': True,
|
||||||
|
'payloadhash': '7061796c6f61642068617368',
|
||||||
|
'epoch': 'epoch',
|
||||||
|
'version': 'version',
|
||||||
|
'buildroot_id': None,
|
||||||
|
'release': 'release',
|
||||||
|
'external_repo_id': 0,
|
||||||
|
'id': 9876,
|
||||||
|
'size': 0,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(self.inserts), 1)
|
||||||
|
insert = self.inserts[0]
|
||||||
|
self.assertEqual(insert.table, 'rpminfo')
|
||||||
|
self.assertEqual(insert.data, data)
|
||||||
|
self.assertEqual(insert.rawdata, {})
|
||||||
|
self.get_build.assert_called_once_with(5566, strict=True)
|
||||||
|
self.assertEqual(self.get_build.call_count, 1)
|
||||||
|
|
@ -17,6 +17,32 @@ class TestListBuilds(unittest.TestCase):
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
|
# defaults
|
||||||
|
self.tables= ['build']
|
||||||
|
self.columns = [
|
||||||
|
'build.id', 'build.completion_time',
|
||||||
|
"date_part('epoch', build.completion_time)",
|
||||||
|
'events.id', 'events.time',
|
||||||
|
"date_part('epoch', events.time)",
|
||||||
|
'build.draft',
|
||||||
|
'build.epoch',
|
||||||
|
'build.extra', 'package.name',
|
||||||
|
"package.name || '-' || build.version || '-' || "
|
||||||
|
"build.release", 'users.id', 'users.name', 'package.id',
|
||||||
|
'package.name', 'build.release', 'build.source',
|
||||||
|
'build.start_time', "date_part('epoch', build.start_time)",
|
||||||
|
'build.state', 'build.task_id', 'build.version',
|
||||||
|
'volume.id', 'volume.name'
|
||||||
|
]
|
||||||
|
self.clauses = ['package.id = %(packageID)i']
|
||||||
|
self.joins = [
|
||||||
|
'LEFT JOIN events ON build.create_event = events.id',
|
||||||
|
'LEFT JOIN package ON build.pkg_id = package.id',
|
||||||
|
'LEFT JOIN volume ON build.volume_id = volume.id',
|
||||||
|
'LEFT JOIN users ON build.owner = users.id'
|
||||||
|
]
|
||||||
|
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.exports = kojihub.RootExports()
|
self.exports = kojihub.RootExports()
|
||||||
self.query_executeOne = mock.MagicMock()
|
self.query_executeOne = mock.MagicMock()
|
||||||
|
|
@ -41,7 +67,8 @@ class TestListBuilds(unittest.TestCase):
|
||||||
'task_id': 879,
|
'task_id': 879,
|
||||||
'version': '11',
|
'version': '11',
|
||||||
'volume_id': 0,
|
'volume_id': 0,
|
||||||
'volume_name': 'DEFAULT'}]
|
'volume_name': 'DEFAULT',
|
||||||
|
'draft': False},]
|
||||||
|
|
||||||
def test_wrong_package(self):
|
def test_wrong_package(self):
|
||||||
package = 'test-package'
|
package = 'test-package'
|
||||||
|
|
@ -58,26 +85,27 @@ class TestListBuilds(unittest.TestCase):
|
||||||
self.assertEqual(len(self.queries), 1)
|
self.assertEqual(len(self.queries), 1)
|
||||||
args, kwargs = self.QueryProcessor.call_args
|
args, kwargs = self.QueryProcessor.call_args
|
||||||
qp = QP(**kwargs)
|
qp = QP(**kwargs)
|
||||||
self.assertEqual(qp.tables, ['build'])
|
self.assertEqual(qp.tables, self.tables)
|
||||||
self.assertEqual(qp.columns, ['build.id', 'build.completion_time',
|
self.assertEqual(qp.columns, self.columns)
|
||||||
"date_part('epoch', build.completion_time)",
|
self.assertEqual(qp.clauses, self.clauses)
|
||||||
'events.id', 'events.time',
|
self.assertEqual(qp.joins, self.joins)
|
||||||
"date_part('epoch', events.time)", 'build.epoch',
|
|
||||||
'build.extra', 'package.name',
|
|
||||||
"package.name || '-' || build.version || '-' || "
|
|
||||||
"build.release", 'users.id', 'users.name', 'package.id',
|
|
||||||
'package.name', 'build.release', 'build.source',
|
|
||||||
'build.start_time', "date_part('epoch', build.start_time)",
|
|
||||||
'build.state', 'build.task_id', 'build.version',
|
|
||||||
'volume.id', 'volume.name'])
|
|
||||||
self.assertEqual(qp.clauses, ['package.id = %(packageID)i'])
|
|
||||||
self.assertEqual(qp.joins, ['LEFT JOIN events ON build.create_event = events.id',
|
|
||||||
'LEFT JOIN package ON build.pkg_id = package.id',
|
|
||||||
'LEFT JOIN volume ON build.volume_id = volume.id',
|
|
||||||
'LEFT JOIN users ON build.owner = users.id'])
|
|
||||||
|
|
||||||
def test_wrong_user(self):
|
def test_wrong_user(self):
|
||||||
user = 'test-user'
|
user = 'test-user'
|
||||||
self.get_user.return_value = None
|
self.get_user.return_value = None
|
||||||
rv = self.exports.listBuilds(userID=user)
|
rv = self.exports.listBuilds(userID=user)
|
||||||
self.assertEqual(rv, [])
|
self.assertEqual(rv, [])
|
||||||
|
|
||||||
|
def test_draft(self):
|
||||||
|
package = 'test-package'
|
||||||
|
package_id = 1
|
||||||
|
self.get_package_id.return_value = package_id
|
||||||
|
self.query_executeOne.return_value = None
|
||||||
|
self.exports.listBuilds(packageID=package, draft=1)
|
||||||
|
self.assertEqual(len(self.queries), 1)
|
||||||
|
args, kwargs = self.QueryProcessor.call_args
|
||||||
|
qp = QP(**kwargs)
|
||||||
|
self.assertEqual(qp.tables, self.tables)
|
||||||
|
self.assertEqual(qp.columns, self.columns)
|
||||||
|
self.assertEqual(qp.clauses, ['draft IS TRUE'] + self.clauses)
|
||||||
|
self.assertEqual(qp.joins, self.joins)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import mock
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import koji
|
import koji
|
||||||
|
from koji.util import dslice
|
||||||
import kojihub
|
import kojihub
|
||||||
|
|
||||||
IP = kojihub.InsertProcessor
|
IP = kojihub.InsertProcessor
|
||||||
|
|
@ -23,6 +24,7 @@ class TestNewBuild(unittest.TestCase):
|
||||||
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
||||||
self.recycle_build = mock.patch('kojihub.kojihub.recycle_build').start()
|
self.recycle_build = mock.patch('kojihub.kojihub.recycle_build').start()
|
||||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||||
|
self.find_build_id = mock.patch('kojihub.kojihub.find_build_id').start()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
mock.patch.stopall()
|
mock.patch.stopall()
|
||||||
|
|
@ -64,6 +66,7 @@ class TestNewBuild(unittest.TestCase):
|
||||||
'start_time': 'NOW',
|
'start_time': 'NOW',
|
||||||
'state': 1,
|
'state': 1,
|
||||||
'task_id': None,
|
'task_id': None,
|
||||||
|
'draft': False,
|
||||||
'version': 'test_version',
|
'version': 'test_version',
|
||||||
'volume_id': 0
|
'volume_id': 0
|
||||||
})
|
})
|
||||||
|
|
@ -156,3 +159,48 @@ class TestNewBuild(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(len(self.inserts), 0)
|
self.assertEqual(len(self.inserts), 0)
|
||||||
self.assertEqual("No such build extra data: %(extra)r" % data, str(cm.exception))
|
self.assertEqual("No such build extra data: %(extra)r" % data, str(cm.exception))
|
||||||
|
|
||||||
|
def test_draft(self):
|
||||||
|
data = {
|
||||||
|
'owner': 123456,
|
||||||
|
'name': 'test_name',
|
||||||
|
'version': 'test_version',
|
||||||
|
'release': 'test_release',
|
||||||
|
'epoch': 'test_epoch',
|
||||||
|
'draft': True
|
||||||
|
}
|
||||||
|
insert_data = {
|
||||||
|
'completion_time': 'NOW',
|
||||||
|
'epoch': 'test_epoch',
|
||||||
|
'extra': '{"draft": {"target_release": "test_release", "promoted": false}}',
|
||||||
|
'id': 108,
|
||||||
|
'owner': 123,
|
||||||
|
'pkg_id': 54,
|
||||||
|
'release': 'test_release#draft_108',
|
||||||
|
'source': None,
|
||||||
|
'start_time': 'NOW',
|
||||||
|
'state': 1,
|
||||||
|
'task_id': None,
|
||||||
|
'draft': True,
|
||||||
|
'version': 'test_version',
|
||||||
|
'volume_id': 0
|
||||||
|
}
|
||||||
|
self.nextval.return_value = 108
|
||||||
|
self.new_package.return_value = 54
|
||||||
|
self.get_user.return_value = {'id': 123}
|
||||||
|
self.find_build_id.return_value = None
|
||||||
|
|
||||||
|
kojihub.new_build(data)
|
||||||
|
|
||||||
|
self.assertEqual(len(self.inserts), 1)
|
||||||
|
insert = self.inserts[0]
|
||||||
|
self.assertEqual(insert.table, 'build')
|
||||||
|
self.assertEqual(insert.data, insert_data)
|
||||||
|
self.get_build.assert_called_once_with(108, strict=True)
|
||||||
|
self.assertEqual(self.get_build.call_count, 1)
|
||||||
|
self.find_build_id.assert_called_once_with(
|
||||||
|
{
|
||||||
|
'name': 'test_name',
|
||||||
|
'version': 'test_version',
|
||||||
|
'release': 'test_release#draft_108'
|
||||||
|
})
|
||||||
|
|
|
||||||
179
tests/test_hub/test_promote_build.py
Normal file
179
tests/test_hub/test_promote_build.py
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import mock
|
||||||
|
import unittest
|
||||||
|
import koji
|
||||||
|
import kojihub
|
||||||
|
|
||||||
|
|
||||||
|
UP = kojihub.UpdateProcessor
|
||||||
|
|
||||||
|
|
||||||
|
class TestPromoteBuild(unittest.TestCase):
|
||||||
|
|
||||||
|
def getUpdate(self, *args, **kwargs):
|
||||||
|
update = UP(*args, **kwargs)
|
||||||
|
update.execute = mock.MagicMock()
|
||||||
|
self.updates.append(update)
|
||||||
|
return update
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.exports = kojihub.RootExports()
|
||||||
|
self.UpdateProcessor = mock.patch('kojihub.kojihub.UpdateProcessor',
|
||||||
|
side_effect=self.getUpdate).start()
|
||||||
|
self.updates = []
|
||||||
|
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||||
|
self.context.session.assertLogin = mock.MagicMock()
|
||||||
|
self.user = {'id': 1, 'name': 'jdoe'}
|
||||||
|
self.getLoggedInUser = mock.patch.object(self.exports, 'getLoggedInUser',
|
||||||
|
return_value=self.user).start()
|
||||||
|
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
||||||
|
self.assert_policy = mock.patch('kojihub.kojihub.assert_policy').start()
|
||||||
|
self.apply_volume_policy = mock.patch('kojihub.kojihub.apply_volume_policy',
|
||||||
|
return_value=None).start()
|
||||||
|
self.move_and_symlink = mock.patch('kojihub.kojihub.move_and_symlink').start()
|
||||||
|
self.ensure_volume_symlink = mock.patch('kojihub.kojihub.ensure_volume_symlink').start()
|
||||||
|
self.list_tags = mock.patch('kojihub.kojihub.list_tags',
|
||||||
|
return_value=[{'id': 101}]).start()
|
||||||
|
self.set_tag_update = mock.patch('kojihub.kojihub.set_tag_update').start()
|
||||||
|
self.encode_datetime = mock.patch('kojihub.kojihub.encode_datetime', return_value='NOW').start()
|
||||||
|
self._now = datetime.datetime.now()
|
||||||
|
self._datetime = mock.patch('kojihub.kojihub.datetime.datetime').start()
|
||||||
|
self.now = self._datetime.now = mock.MagicMock(return_value=self._now)
|
||||||
|
|
||||||
|
self.draft_build = {
|
||||||
|
'id': 1,
|
||||||
|
'name': 'foo',
|
||||||
|
'version': 'bar',
|
||||||
|
'release': 'dftrel_1',
|
||||||
|
'extra': {
|
||||||
|
'draft': {
|
||||||
|
'promoted': False,
|
||||||
|
'target_release': 'tgtrel_1'
|
||||||
|
}},
|
||||||
|
'draft': True,
|
||||||
|
'volume_id': 99,
|
||||||
|
'volume_name': 'X'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.new_build = {
|
||||||
|
# no check on the info
|
||||||
|
'id': 1,
|
||||||
|
'name': 'foo',
|
||||||
|
'version': 'bar',
|
||||||
|
'release': 'tgtrel_1',
|
||||||
|
'volume_name': 'X'
|
||||||
|
}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
mock.patch.stopall()
|
||||||
|
|
||||||
|
def test_promote_build_valid(self):
|
||||||
|
self.get_build.side_effect = [
|
||||||
|
self.draft_build,
|
||||||
|
None,
|
||||||
|
self.new_build
|
||||||
|
]
|
||||||
|
|
||||||
|
extra = json.dumps(
|
||||||
|
{
|
||||||
|
'draft': {
|
||||||
|
'promoted': True,
|
||||||
|
'target_release': 'tgtrel_1',
|
||||||
|
'old_release': 'dftrel_1',
|
||||||
|
'promotion_time': 'NOW',
|
||||||
|
'promotion_ts': self._now.timestamp(),
|
||||||
|
'promoter': self.user['name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
ret = self.exports.promoteBuild('a-draft-build', strict=True)
|
||||||
|
self.assertEqual(ret, self.new_build)
|
||||||
|
self.assertEqual(len(self.updates), 1)
|
||||||
|
update = self.updates[0]
|
||||||
|
self.assertEqual(update.table, 'build')
|
||||||
|
self.assertEqual(update.values, self.draft_build)
|
||||||
|
self.assertEqual(update.data, {'draft': False,
|
||||||
|
'release': 'tgtrel_1',
|
||||||
|
'extra': extra})
|
||||||
|
self.assertEqual(update.rawdata, {})
|
||||||
|
self.assertEqual(update.clauses, ['id=%(id)i'])
|
||||||
|
|
||||||
|
def test_promote_build_not_draft(self):
|
||||||
|
self.get_build.return_value = {'draft': False}
|
||||||
|
|
||||||
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
|
self.exports.promoteBuild('a-regular-build', strict=True)
|
||||||
|
self.assertEqual(str(cm.exception), "Not a draft build: {'draft': False}")
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
|
||||||
|
ret = self.exports.promoteBuild('a-regular-build', strict=False)
|
||||||
|
self.assertIsNone(ret)
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
|
||||||
|
def test_promote_build_no_target_release(self):
|
||||||
|
draft = {
|
||||||
|
'id': 1,
|
||||||
|
'name': 'foo',
|
||||||
|
'version': 'bar',
|
||||||
|
'release': 'dftrel_1',
|
||||||
|
'extra': {
|
||||||
|
'draft': {
|
||||||
|
'promoted': False
|
||||||
|
# no target_release
|
||||||
|
}},
|
||||||
|
'draft': True,
|
||||||
|
'volume_id': 99,
|
||||||
|
'volume_name': 'X'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get_build.return_value = draft
|
||||||
|
|
||||||
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
|
self.exports.promoteBuild('a-regular-build', strict=True)
|
||||||
|
self.assertEqual(str(cm.exception), f"draft.target_release not found in extra of build: {draft}")
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
|
||||||
|
ret = self.exports.promoteBuild('a-regular-build', strict=False)
|
||||||
|
self.assertIsNone(ret)
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
|
||||||
|
def test_promote_build_target_build_exists(self):
|
||||||
|
old = {
|
||||||
|
'id': 'any'
|
||||||
|
}
|
||||||
|
self.get_build.side_effect = [self.draft_build, old]
|
||||||
|
|
||||||
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
|
self.exports.promoteBuild('a-regular-build', strict=True)
|
||||||
|
self.assertEqual(str(cm.exception), f"Target build already exists: {old}")
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
self.get_build.assert_called_with({
|
||||||
|
'name': 'foo',
|
||||||
|
'version': 'bar',
|
||||||
|
'release': 'tgtrel_1'
|
||||||
|
})
|
||||||
|
|
||||||
|
self.get_build.reset_mock()
|
||||||
|
self.get_build.side_effect = [self.draft_build, old]
|
||||||
|
ret = self.exports.promoteBuild('a-regular-build', strict=False)
|
||||||
|
self.assertIsNone(ret)
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
|
||||||
|
def test_promote_build_volume_changed(self):
|
||||||
|
self.get_build.side_effect = [self.draft_build, None]
|
||||||
|
self.apply_volume_policy.return_value = {
|
||||||
|
'id': 100,
|
||||||
|
'name': 'Y'
|
||||||
|
}
|
||||||
|
with self.assertRaises(koji.GenericError) as cm:
|
||||||
|
self.exports.promoteBuild('a-regular-build', strict=True)
|
||||||
|
self.assertEqual(str(cm.exception), f"Denial as volume will be changed to Y")
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
|
||||||
|
self.get_build.reset_mock()
|
||||||
|
self.get_build.side_effect = [self.draft_build, None]
|
||||||
|
ret = self.exports.promoteBuild('a-regular-build', strict=False)
|
||||||
|
self.assertIsNone(ret)
|
||||||
|
self.assertEqual(len(self.updates), 0)
|
||||||
|
|
@ -31,7 +31,7 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
self.tag_name = 'test-tag'
|
self.tag_name = 'test-tag'
|
||||||
self.columns = ['tag.id', 'tag.name', 'build.id', 'build.version', 'build.release',
|
self.columns = ['tag.id', 'tag.name', 'build.id', 'build.version', 'build.release',
|
||||||
'build.epoch', 'build.state', 'build.completion_time', 'build.start_time',
|
'build.epoch', 'build.state', 'build.completion_time', 'build.start_time',
|
||||||
'build.task_id', 'users.id', 'users.name', 'events.id', 'events.time',
|
'build.task_id', 'build.draft', 'users.id', 'users.name', 'events.id', 'events.time',
|
||||||
'volume.id', 'volume.name', 'package.id', 'package.name',
|
'volume.id', 'volume.name', 'package.id', 'package.name',
|
||||||
'package.name || \'-\' || build.version || \'-\' || build.release',
|
'package.name || \'-\' || build.version || \'-\' || build.release',
|
||||||
'tag_listing.create_event']
|
'tag_listing.create_event']
|
||||||
|
|
@ -40,6 +40,7 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
('build.release', 'release'), ('build.epoch', 'epoch'),
|
('build.release', 'release'), ('build.epoch', 'epoch'),
|
||||||
('build.state', 'state'), ('build.completion_time', 'completion_time'),
|
('build.state', 'state'), ('build.completion_time', 'completion_time'),
|
||||||
('build.start_time', 'start_time'), ('build.task_id', 'task_id'),
|
('build.start_time', 'start_time'), ('build.task_id', 'task_id'),
|
||||||
|
('build.draft', 'draft'),
|
||||||
('users.id', 'owner_id'), ('users.name', 'owner_name'),
|
('users.id', 'owner_id'), ('users.name', 'owner_name'),
|
||||||
('events.id', 'creation_event_id'), ('events.time', 'creation_time'),
|
('events.id', 'creation_event_id'), ('events.time', 'creation_time'),
|
||||||
('volume.id', 'volume_id'), ('volume.name', 'volume_name'),
|
('volume.id', 'volume_id'), ('volume.name', 'volume_name'),
|
||||||
|
|
@ -54,7 +55,7 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
'volume ON volume.id = build.volume_id',
|
'volume ON volume.id = build.volume_id',
|
||||||
'users ON users.id = build.owner', ]
|
'users ON users.id = build.owner', ]
|
||||||
self.aliases = ['tag_id', 'tag_name', 'id', 'build_id', 'version', 'release', 'epoch',
|
self.aliases = ['tag_id', 'tag_name', 'id', 'build_id', 'version', 'release', 'epoch',
|
||||||
'state', 'completion_time', 'start_time', 'task_id', 'owner_id',
|
'state', 'completion_time', 'start_time', 'task_id', 'draft', 'owner_id',
|
||||||
'owner_name', 'creation_event_id', 'creation_time', 'volume_id',
|
'owner_name', 'creation_event_id', 'creation_time', 'volume_id',
|
||||||
'volume_name', 'package_id', 'package_name', 'name', 'nvr', 'create_event']
|
'volume_name', 'package_id', 'package_name', 'name', 'nvr', 'create_event']
|
||||||
self.clauses = ['(tag_listing.active = TRUE)',
|
self.clauses = ['(tag_listing.active = TRUE)',
|
||||||
|
|
@ -83,7 +84,7 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
'package': None, 'packages': self.package_list,
|
'package': None, 'packages': self.package_list,
|
||||||
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
||||||
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
||||||
'type': None
|
'type': None, 'draft': 3
|
||||||
}
|
}
|
||||||
self.assertEqual(query.tables, self.tables)
|
self.assertEqual(query.tables, self.tables)
|
||||||
self.assertEqual(query.joins, self.joins)
|
self.assertEqual(query.joins, self.joins)
|
||||||
|
|
@ -119,7 +120,7 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
'package': self.pkg_name, 'packages': self.package_list,
|
'package': self.pkg_name, 'packages': self.package_list,
|
||||||
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
||||||
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
||||||
'type': 'maven'}
|
'type': 'maven', 'draft': 3}
|
||||||
self.assertEqual(query.tables, self.tables)
|
self.assertEqual(query.tables, self.tables)
|
||||||
self.assertEqual(query.joins, joins)
|
self.assertEqual(query.joins, joins)
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
|
|
@ -148,7 +149,7 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
'package': None, 'packages': self.package_list,
|
'package': None, 'packages': self.package_list,
|
||||||
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
||||||
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
||||||
'type': 'win'}
|
'type': 'win', 'draft': 3}
|
||||||
self.assertEqual(query.tables, self.tables)
|
self.assertEqual(query.tables, self.tables)
|
||||||
self.assertEqual(query.joins, joins)
|
self.assertEqual(query.joins, joins)
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
|
|
@ -177,7 +178,7 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
'package': None, 'packages': self.package_list,
|
'package': None, 'packages': self.package_list,
|
||||||
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
||||||
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
||||||
'type': 'image'}
|
'type': 'image', 'draft': 3}
|
||||||
self.assertEqual(query.tables, self.tables)
|
self.assertEqual(query.tables, self.tables)
|
||||||
self.assertEqual(query.joins, joins)
|
self.assertEqual(query.joins, joins)
|
||||||
self.assertEqual(set(query.columns), set(columns))
|
self.assertEqual(set(query.columns), set(columns))
|
||||||
|
|
@ -212,10 +213,35 @@ class TestReadTaggedBuilds(unittest.TestCase):
|
||||||
'package': None, 'packages': self.package_list,
|
'package': None, 'packages': self.package_list,
|
||||||
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
||||||
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
||||||
'type': type}
|
'type': type, 'draft': 3}
|
||||||
self.assertEqual(query.tables, self.tables)
|
self.assertEqual(query.tables, self.tables)
|
||||||
self.assertEqual(query.joins, joins)
|
self.assertEqual(query.joins, joins)
|
||||||
self.assertEqual(set(query.columns), set(self.columns))
|
self.assertEqual(set(query.columns), set(self.columns))
|
||||||
self.assertEqual(set(query.aliases), set(self.aliases))
|
self.assertEqual(set(query.aliases), set(self.aliases))
|
||||||
self.assertEqual(set(query.clauses), set(self.clauses))
|
self.assertEqual(set(query.clauses), set(self.clauses))
|
||||||
self.assertEqual(query.values, values)
|
self.assertEqual(query.values, values)
|
||||||
|
|
||||||
|
def test_get_tagged_builds_draft(self):
|
||||||
|
self.readPackageList.return_value = self.package_list
|
||||||
|
kojihub.readTaggedBuilds(self.tag_name, draft=koji.DRAFT_FLAG.DRAFT)
|
||||||
|
|
||||||
|
self.assertEqual(len(self.queries), 1)
|
||||||
|
query = self.queries[0]
|
||||||
|
|
||||||
|
clauses = copy.deepcopy(self.clauses)
|
||||||
|
clauses.extend(['draft IS TRUE'])
|
||||||
|
|
||||||
|
values = {'clauses': clauses, 'event': None, 'extra': False, 'fields': self.fields,
|
||||||
|
'inherit': False, 'joins': self.joins, 'latest': False, 'owner': None,
|
||||||
|
'package': None, 'packages': self.package_list,
|
||||||
|
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
|
||||||
|
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
|
||||||
|
'type': None, 'draft': koji.DRAFT_FLAG.DRAFT
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertEqual(query.tables, self.tables)
|
||||||
|
self.assertEqual(query.joins, self.joins)
|
||||||
|
self.assertEqual(set(query.columns), set(self.columns))
|
||||||
|
self.assertEqual(set(query.aliases), set(self.aliases))
|
||||||
|
self.assertEqual(set(query.clauses), set(clauses))
|
||||||
|
self.assertEqual(query.values, values)
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,13 @@ class TestReadTaggedRPMS(unittest.TestCase):
|
||||||
self.readTaggedBuilds = mock.patch('kojihub.kojihub.readTaggedBuilds').start()
|
self.readTaggedBuilds = mock.patch('kojihub.kojihub.readTaggedBuilds').start()
|
||||||
self.tag_name = 'test-tag'
|
self.tag_name = 'test-tag'
|
||||||
self.columns = ['rpminfo.name', 'rpminfo.version', 'rpminfo.release', 'rpminfo.arch',
|
self.columns = ['rpminfo.name', 'rpminfo.version', 'rpminfo.release', 'rpminfo.arch',
|
||||||
'rpminfo.id', 'rpminfo.epoch', 'rpminfo.payloadhash', 'rpminfo.size',
|
'rpminfo.id', 'rpminfo.epoch', 'rpminfo.draft', 'rpminfo.payloadhash',
|
||||||
'rpminfo.buildtime', 'rpminfo.buildroot_id', 'rpminfo.build_id',
|
'rpminfo.size', 'rpminfo.buildtime', 'rpminfo.buildroot_id',
|
||||||
'rpminfo.metadata_only']
|
'rpminfo.build_id', 'rpminfo.metadata_only']
|
||||||
self.joins = ['tag_listing ON rpminfo.build_id = tag_listing.build_id']
|
self.joins = ['tag_listing ON rpminfo.build_id = tag_listing.build_id']
|
||||||
self.aliases = ['name', 'version', 'release', 'arch', 'id', 'epoch', 'payloadhash',
|
self.aliases = ['name', 'version', 'release', 'arch', 'id', 'epoch', 'draft',
|
||||||
'size', 'buildtime', 'buildroot_id', 'build_id', 'metadata_only']
|
'payloadhash', 'size', 'buildtime', 'buildroot_id', 'build_id',
|
||||||
|
'metadata_only']
|
||||||
self.clauses = ['(tag_listing.active = TRUE)',
|
self.clauses = ['(tag_listing.active = TRUE)',
|
||||||
'tag_id=%(tagid)s']
|
'tag_id=%(tagid)s']
|
||||||
self.tables = ['rpminfo']
|
self.tables = ['rpminfo']
|
||||||
|
|
@ -101,3 +102,20 @@ class TestReadTaggedRPMS(unittest.TestCase):
|
||||||
self.assertEqual(set(query.aliases), set(aliases))
|
self.assertEqual(set(query.aliases), set(aliases))
|
||||||
self.assertEqual(set(query.clauses), set(clauses))
|
self.assertEqual(set(query.clauses), set(clauses))
|
||||||
self.assertEqual(query.values, values)
|
self.assertEqual(query.values, values)
|
||||||
|
|
||||||
|
def test_get_tagged_rpms_draft(self):
|
||||||
|
self.readTaggedBuilds.return_value = self.build_list
|
||||||
|
kojihub.readTaggedRPMS(self.tag_name, draft=2, extra=False)
|
||||||
|
|
||||||
|
self.assertEqual(len(self.queries), 1)
|
||||||
|
query = self.queries[0]
|
||||||
|
|
||||||
|
clauses = copy.deepcopy(self.clauses)
|
||||||
|
clauses.extend(['rpminfo.draft IS NOT TRUE'])
|
||||||
|
|
||||||
|
self.assertEqual(query.tables, self.tables)
|
||||||
|
self.assertEqual(set(query.columns), set(self.columns))
|
||||||
|
self.assertEqual(set(query.joins), set(self.joins))
|
||||||
|
self.assertEqual(set(query.aliases), set(self.aliases))
|
||||||
|
self.assertEqual(set(query.clauses), set(clauses))
|
||||||
|
self.assertEqual(query.values, {})
|
||||||
|
|
@ -44,6 +44,9 @@ class TestResetBuild(unittest.TestCase):
|
||||||
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
||||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||||
self.context.session.assertPerm = mock.MagicMock()
|
self.context.session.assertPerm = mock.MagicMock()
|
||||||
|
# don't remove anything unexpected
|
||||||
|
self.rmtree = mock.patch('koji.util.rmtree').start()
|
||||||
|
self.unlink = mock.patch('os.unlink').start()
|
||||||
self.build_id = 3
|
self.build_id = 3
|
||||||
self.binfo = {'id': 3, 'state': koji.BUILD_STATES['COMPLETE'], 'name': 'test_nvr',
|
self.binfo = {'id': 3, 'state': koji.BUILD_STATES['COMPLETE'], 'name': 'test_nvr',
|
||||||
'nvr': 'test_nvr-3.3-20.el8', 'version': '3.3', 'release': '20',
|
'nvr': 'test_nvr-3.3-20.el8', 'version': '3.3', 'release': '20',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue