clone-tag --no-delete mode

Fixes: https://pagure.io/koji/issue/1384
This commit is contained in:
Tomas Kopecek 2019-04-03 17:25:36 +02:00
parent 641011760d
commit bd9ce26461
2 changed files with 376 additions and 116 deletions

View file

@ -3377,6 +3377,11 @@ def handle_clone_tag(goptions, session, args):
"the dest tag"))
parser.add_option('--ts', type='int', metavar="TIMESTAMP",
help=_('Clone tag at last event before specific timestamp'))
parser.add_option('--no-delete', action='store_false', dest="delete",
default=True,
help=_("Don't delete any existing content in dest tag. "
"Note, that you can end with older latest builds in dest "
"than in src, if they are already tagged."))
parser.add_option('--event', type='int',
help=_('Clone tag at a specific event'))
parser.add_option('--repo', type='int',
@ -3644,27 +3649,28 @@ def handle_clone_tag(goptions, session, args):
if not options.test:
session.multiCall(batch=options.batch)
# DEL builds. To keep the order we should untag builds at first
if not options.test:
session.multicall = True
for build in bdellist:
# don't delete an inherited build.
if build['tag_name'] == dsttag['name']:
# add missing 'name' field
build['name'] = build['package_name']
chgbldlist.append(('[del]',
build['package_name'],
build['nvr'],
koji.BUILD_STATES[build['state']],
build['owner_name'],
build['tag_name']))
# go on del builds from new tag.
if not options.test:
session.untagBuildBypass(dsttag['name'],
build,
force=options.force,
notify=options.notify)
if not options.test:
session.multiCall(batch=options.batch)
if options.delete:
if not options.test:
session.multicall = True
for build in bdellist:
# don't delete an inherited build.
if build['tag_name'] == dsttag['name']:
# add missing 'name' field
build['name'] = build['package_name']
chgbldlist.append(('[del]',
build['package_name'],
build['nvr'],
koji.BUILD_STATES[build['state']],
build['owner_name'],
build['tag_name']))
# go on del builds from new tag.
if not options.test:
session.untagBuildBypass(dsttag['name'],
build,
force=options.force,
notify=options.notify)
if not options.test:
session.multiCall(batch=options.batch)
# ADD builds.
if not options.test:
session.multicall = True
@ -3715,103 +3721,104 @@ def handle_clone_tag(goptions, session, args):
force=options.force)
if not options.test:
session.multiCall(batch=options.batch)
# DEL packages.
ninhrtpdellist = []
inhrtpdellist = []
for pkg in pdellist:
if pkg['tag_name'] == dsttag['name']:
ninhrtpdellist.append(pkg)
else:
inhrtpdellist.append(pkg)
session.multicall = True
# delete only non-inherited packages.
for pkg in ninhrtpdellist:
# check if package have owned builds inside.
session.listTagged(dsttag['name'],
package=pkg['package_name'],
inherit=False)
bump_builds = session.multiCall(batch=options.batch)
if not options.test:
session.multicall = True
for pkg, [builds] in zip(ninhrtpdellist, bump_builds):
# remove all its builds first if there are any.
for build in builds:
# add missing 'name' field.
build['name'] = build['package_name']
chgbldlist.append(('[del]',
build['package_name'],
build['nvr'],
koji.BUILD_STATES[build['state']],
build['owner_name'],
build['tag_name']))
# so delete latest build(s) from new tag.
if not options.test:
session.untagBuildBypass(dsttag['name'],
build,
force=options.force,
notify=options.notify)
# now safe to remove package itself since we resolved its builds.
chgpkglist.append(('[del]',
pkg['package_name'],
pkg['blocked'],
pkg['owner_name'],
pkg['tag_name']))
if not options.test:
session.packageListRemove(dsttag['name'],
pkg['package_name'],
force=False)
# mark as blocked inherited packages.
for pkg in inhrtpdellist:
chgpkglist.append(('[blk]',
pkg['package_name'],
pkg['blocked'],
pkg['owner_name'],
pkg['tag_name']))
if not options.test:
session.packageListBlock(dsttag['name'], pkg['package_name'])
if not options.test:
session.multiCall(batch=options.batch)
# DEL groups.
if not options.test:
session.multicall = True
for group in gdellist:
# Only delete a group that isn't inherited
if group['tag_id'] == dsttag['id']:
if not options.test:
session.groupListRemove(dsttag['name'],
group['name'],
force=options.force)
for pkg in group['packagelist']:
chggrplist.append(('[del]', pkg['package'], group['name']))
# mark as blocked inherited groups.
else:
if not options.test:
session.groupListBlock(dsttag['name'], group['name'])
for pkg in group['packagelist']:
chggrplist.append(('[blk]', pkg['package'], group['name']))
if not options.test:
session.multiCall(batch=options.batch)
# DEL group pkgs.
if not options.test:
session.multicall = True
for group in grpchanges:
for pkg in grpchanges[group]['dels']:
# Only delete a group that isn't inherited
if not grpchanges[group]['inherited']:
chggrplist.append(('[del]', pkg, group))
if not options.test:
session.groupPackageListRemove(dsttag['name'],
group,
pkg,
force=options.force)
if options.delete:
# DEL packages
ninhrtpdellist = []
inhrtpdellist = []
for pkg in pdellist:
if pkg['tag_name'] == dsttag['name']:
ninhrtpdellist.append(pkg)
else:
chggrplist.append(('[blk]', pkg, group))
inhrtpdellist.append(pkg)
session.multicall = True
# delete only non-inherited packages.
for pkg in ninhrtpdellist:
# check if package have owned builds inside.
session.listTagged(dsttag['name'],
package=pkg['package_name'],
inherit=False)
bump_builds = session.multiCall(batch=options.batch)
if not options.test:
session.multicall = True
for pkg, [builds] in zip(ninhrtpdellist, bump_builds):
# remove all its builds first if there are any.
for build in builds:
# add missing 'name' field.
build['name'] = build['package_name']
chgbldlist.append(('[del]',
build['package_name'],
build['nvr'],
koji.BUILD_STATES[build['state']],
build['owner_name'],
build['tag_name']))
# so delete latest build(s) from new tag.
if not options.test:
session.groupPackageListBlock(dsttag['name'],
group,
pkg)
if not options.test:
session.multiCall(batch=options.batch)
session.untagBuildBypass(dsttag['name'],
build,
force=options.force,
notify=options.notify)
# now safe to remove package itself since we resolved its builds.
chgpkglist.append(('[del]',
pkg['package_name'],
pkg['blocked'],
pkg['owner_name'],
pkg['tag_name']))
if not options.test:
session.packageListRemove(dsttag['name'],
pkg['package_name'],
force=False)
# mark as blocked inherited packages.
for pkg in inhrtpdellist:
chgpkglist.append(('[blk]',
pkg['package_name'],
pkg['blocked'],
pkg['owner_name'],
pkg['tag_name']))
if not options.test:
session.packageListBlock(dsttag['name'], pkg['package_name'])
if not options.test:
session.multiCall(batch=options.batch)
# DEL groups.
if not options.test:
session.multicall = True
for group in gdellist:
# Only delete a group that isn't inherited
if group['tag_id'] == dsttag['id']:
if not options.test:
session.groupListRemove(dsttag['name'],
group['name'],
force=options.force)
for pkg in group['packagelist']:
chggrplist.append(('[del]', pkg['package'], group['name']))
# mark as blocked inherited groups.
else:
if not options.test:
session.groupListBlock(dsttag['name'], group['name'])
for pkg in group['packagelist']:
chggrplist.append(('[blk]', pkg['package'], group['name']))
if not options.test:
session.multiCall(batch=options.batch)
# DEL group pkgs.
if not options.test:
session.multicall = True
for group in grpchanges:
for pkg in grpchanges[group]['dels']:
# Only delete a group that isn't inherited
if not grpchanges[group]['inherited']:
chggrplist.append(('[del]', pkg, group))
if not options.test:
session.groupPackageListRemove(dsttag['name'],
group,
pkg,
force=options.force)
else:
chggrplist.append(('[blk]', pkg, group))
if not options.test:
session.groupPackageListBlock(dsttag['name'],
group,
pkg)
if not options.test:
session.multiCall(batch=options.batch)
# print final list of actions.
if options.verbose:
pfmt = ' %-7s %-28s %-10s %-10s %-10s\n'

View file

@ -622,6 +622,256 @@ List of changes:
[blk] cpkg group2
""")
@mock.patch('sys.stdout', new_callable=six.StringIO)
def test_handle_clone_tag_existing_dsttag_add(self, stdout):
args = ['src-tag', 'dst-tag', '--all', '-v', '--no-delete']
self.session.multiCall.return_value = []
self.session.listPackages.side_effect = [[{'package_id': 1,
'package_name': 'pkg1',
'blocked': False,
'owner_name': 'userA',
'tag_name': 'src-tag',
'extra_arches': None},
{'package_id': 2,
'package_name': 'pkg2',
'blocked': True,
'owner_name': 'userB',
'tag_name': 'src-tag-p',
'extra_arches': 'arch3 arch4'},
{'package_id': 3,
'package_name': 'apkg',
'blocked': False,
'owner_name': 'userA',
'tag_name': 'src-tag-p',
'extra_arches': 'arch4'}],
[{'package_id': 1,
'package_name': 'pkg1',
'blocked': False,
'owner_name': 'userA',
'tag_name': 'src-tag',
'extra_arches': None},
{'package_id': 3,
'package_name': 'apkg',
'blocked': False,
'owner_name': 'userA',
'tag_name': 'src-tag-p',
'extra_arches': 'arch4'},
{'package_id': 4,
'package_name': 'bpkg',
'blocked': False,
'owner_name': 'userC',
'tag_name': 'src-tag',
'extra_arches': 'arch4'},
{'package_id': 5,
'package_name': 'cpkg',
'blocked': True,
'owner_name': 'userC',
'tag_name': 'src-tag-p',
'extra_arches': 'arch4'},
{'package_id': 6,
'package_name': 'dpkg',
'blocked': True,
'owner_name': 'userC',
'tag_name': 'src-tag',
'extra_arches': 'arch4'}
]]
self.session.listTagged.side_effect = [[{'package_name': 'pkg1',
'nvr': 'pkg1-1.1-2',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'src-tag'},
{'package_name': 'pkg1',
'nvr': 'pkg1-1.0-2',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'src-tag'},
{'package_name': 'pkg1',
'nvr': 'pkg1-0.1-1',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'src-tag'},
{'package_name': 'pkg1',
'nvr': 'pkg1-1.0-1',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'src-tag'},
{'package_name': 'pkg2',
'nvr': 'pkg2-1.0-1',
'state': 2,
'owner_name': 'b_owner',
'tag_name': 'src-tag-p'}
],
[{'package_name': 'pkg1',
'nvr': 'pkg1-2.1-2',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'dst-tag'},
{'package_name': 'pkg1',
'nvr': 'pkg1-1.0-1',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'dst-tag'},
{'package_name': 'pkg1',
'nvr': 'pkg1-0.1-1',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'dst-tag'},
{'package_name': 'pkg2',
'nvr': 'pkg2-1.0-1',
'state': 2,
'owner_name': 'b_owner',
'tag_name': 'dst-tag'},
{'package_name': 'pkg3',
'nvr': 'pkg3-1.0-1',
'state': 1,
'owner_name': 'b_owner',
'tag_name': 'dst-tag'}
]]
self.session.getTagGroups.side_effect = [[{'name': 'group1',
'tag_id': 1,
'packagelist': [
{'package': 'pkg1',
'blocked': False},
{'package': 'pkg2',
'blocked': False},
{'package': 'pkg3',
'blocked': False},
{'package': 'pkg4',
'blocked': False}
]},
{'name': 'group2',
'tag_id': 1,
'packagelist': [
{'package': 'apkg',
'blocked': False},
{'package': 'bpkg',
'blocked': False}]
}],
[{'name': 'group1',
'tag_id': 2,
'packagelist': [
{'package': 'pkg1',
'blocked': False},
{'package': 'pkg5',
'blocked': False}
]},
{'name': 'group2',
'tag_id': 3,
'packagelist': [
{'package': 'apkg',
'blocked': False},
{'package': 'cpkg',
'blocked': False}]},
{'name': 'group3',
'tag_id': 2,
'packagelist': [
{'package': 'cpkg',
'blocked': False},
{'package': 'dpkg',
'blocked': False}]},
{'name': 'group4',
'tag_id': 3,
'packagelist': [
{'package': 'epkg',
'blocked': False},
{'package': 'fpkg',
'blocked': False}]}
]]
self.session.getTag.side_effect = [{'id': 1,
'name': 'src-tag',
'arches': 'arch1 arch2',
'perm_id': 1,
'maven_support': False,
'maven_include_all': True,
'locked': False},
{'id': 2,
'name': 'dst-tag',
'arches': 'arch1 arch2',
'perm_id': 1,
'maven_support': False,
'maven_include_all': True,
'locked': False}]
handle_clone_tag(self.options, self.session, args)
self.activate_session.assert_called_once()
self.session.assert_has_calls([call.hasPerm('admin'),
call.getTag('src-tag'),
call.getTag('dst-tag'),
call.listPackages(event=None,
inherited=True,
tagID=1),
call.listPackages(inherited=True,
tagID=2),
call.listTagged(1, event=None,
inherit=None,
latest=None),
call.listTagged(2, inherit=False,
latest=False),
call.getTagGroups('src-tag',
event=None),
call.getTagGroups('dst-tag'),
call.packageListAdd('dst-tag', 'pkg2',
block=True,
extra_arches='arch3 arch4',
owner='userB'),
call.multiCall(batch=1000),
call.tagBuildBypass('dst-tag', {
'owner_name': 'b_owner',
'nvr': 'pkg1-0.1-1',
'package_name': 'pkg1', 'state': 1,
'tag_name': 'src-tag',
'name': 'pkg1'}, force=None),
call.tagBuildBypass('dst-tag', {
'owner_name': 'b_owner',
'nvr': 'pkg1-1.0-2',
'package_name': 'pkg1', 'state': 1,
'tag_name': 'src-tag',
'name': 'pkg1'}, force=None),
call.tagBuildBypass('dst-tag', {
'owner_name': 'b_owner',
'nvr': 'pkg1-1.1-2',
'package_name': 'pkg1', 'state': 1,
'tag_name': 'src-tag',
'name': 'pkg1'}, force=None),
call.multiCall(batch=1000),
call.multiCall(batch=1000),
call.groupPackageListAdd('dst-tag',
'group1',
'pkg2',
force=None),
call.groupPackageListAdd('dst-tag',
'group1',
'pkg3',
force=None),
call.groupPackageListAdd('dst-tag',
'group1',
'pkg4',
force=None),
call.groupPackageListAdd('dst-tag',
'group2',
'bpkg',
force=None),
call.multiCall(batch=1000)])
self.assert_console_message(stdout, """
List of changes:
Action Package Blocked Owner From Tag
------- ---------------------------- ---------- ---------- ----------
[add] pkg2 True userB src-tag-p
Action From/To Package Build(s) State Owner From Tag
------- ---------------------------- ---------------------------------------- ---------- ---------- ----------
[add] pkg1 pkg1-0.1-1 COMPLETE b_owner src-tag
[add] pkg1 pkg1-1.0-2 COMPLETE b_owner src-tag
[add] pkg1 pkg1-1.1-2 COMPLETE b_owner src-tag
Action Package Group
------- ---------------------------- ----------------------------
[new] pkg2 group1
[new] pkg3 group1
[new] pkg4 group1
[new] bpkg group2
""")
def test_handle_clone_tag_help(self):
self.assert_help(
handle_clone_tag,
@ -640,6 +890,9 @@ Options:
--inherit-builds Include all builds inherited into the source tag into the
dest tag
--ts=TIMESTAMP Clone tag at last event before specific timestamp
--no-delete Don't delete any existing content in dest tag. Note, that
you can end with older latest builds in dest than in src,
if they are already tagged.
--event=EVENT Clone tag at a specific event
--repo=REPO Clone tag at a specific repo event
-v, --verbose show changes