virtually accept: virtually accept the delete request

Implementation virtually accept the delete request, the delete request will be
added another delreq-review review and also wipes the binary in the main
project, the backend will sync 'nothing' to ToTest and Snapshot after all. Once
all repository has no binary then remove the package in real.
This commit is contained in:
Max Lin 2017-09-26 18:53:05 +08:00
parent 4011159df4
commit 5611411a6f
3 changed files with 113 additions and 19 deletions

View File

@ -3,9 +3,9 @@ import urllib2
import warnings
from xml.etree import cElementTree as ET
from osc.core import change_request_state
from osc.core import change_request_state, show_package_meta, wipebinaries
from osc.core import http_GET, http_PUT, http_DELETE, http_POST
from osc.core import delete_package
from osc.core import delete_package, search, set_devel_project
from datetime import date
@ -14,7 +14,40 @@ class AcceptCommand(object):
self.api = api
def find_new_requests(self, project):
query = "match=state/@name='new'+and+(action/target/@project='{}'+and+action/@type='submit')".format(project)
query = "match=state/@name='new'+and+action/target/@project='{}'".format(project)
url = self.api.makeurl(['search', 'request'], query)
f = http_GET(url)
root = ET.parse(f).getroot()
rqs = []
for rq in root.findall('request'):
pkgs = []
act_type = None
actions = rq.findall('action')
for action in actions:
act_type = action.get('type')
targets = action.findall('target')
for t in targets:
pkgs.append(str(t.get('package')))
rqs.append({'id': int(rq.get('id')), 'packages': pkgs, 'type': act_type})
return rqs
def virtual_accept_request_has_no_binary(self, project, package):
filelist = self.api.get_filelist_for_package(pkgname=package, project=self.api.project, expand='1', extension='spec')
pkgs = self.api.extract_specfile_short(filelist)
for pkg in pkgs:
query = {'view': 'binarylist', 'package': pkg, 'multibuild': '1'}
pkg_binarylist = ET.parse(http_GET(self.api.makeurl(['build', project, '_result'], query=query))).getroot()
for binary in pkg_binarylist.findall('./result/binarylist/binary'):
return False
return True
def find_virtually_accepted_requests(self, project):
query = "match=state/@name='review'+and+(action/target/@project='{}'+and+action/@type='delete')+and+(review/@state='new'+and+review/@by_group='{}')".format(project, self.api.delreq_review)
url = self.api.makeurl(['search', 'request'], query)
f = http_GET(url)
@ -51,6 +84,24 @@ class AcceptCommand(object):
content = ET.tostring(root)
http_PUT(url + '?comment=accept+command+update', data=content)
def virtually_accept_delete(self, request_id, package):
self.api.add_review(request_id, by_group=self.api.delreq_review, msg='Request accepted. Cleanup in progress - DO NOT REVOKE!')
filelist = self.api.get_filelist_for_package(pkgname=package, project=self.api.project, expand='1', extension='spec')
pkgs = self.api.extract_specfile_short(filelist)
# Disable build and wipes the binary to the package and the sub-package
for pkg in pkgs:
meta = show_package_meta(self.api.apiurl, self.api.project, pkg)
meta = ''.join(meta)
# Update package meta to disable build
self.api.create_package_container(self.api.project, pkg, meta=meta, disable_build=True)
wipebinaries(self.api.apiurl, self.api.project, package=pkg, repo=self.api.main_repo)
# Remove package from Rings
if self.api.ring_packages.get(pkg):
delete_package(self.api.apiurl, self.api.ring_packages.get(pkg), pkg, force=True, msg="Cleanup package in Rings")
def perform(self, project, force=False):
"""Accept the staging project for review and submit to Factory /
openSUSE 13.2 ...
@ -73,16 +124,22 @@ class AcceptCommand(object):
self.api.rm_from_prj(project, request_id=req['id'], msg='ready to accept')
packages.append(req['package'])
msg = 'Accepting staging review for {}'.format(req['package'])
print(msg)
oldspecs = self.api.get_filelist_for_package(pkgname=req['package'],
project=self.api.project,
extension='spec')
change_request_state(self.api.apiurl,
str(req['id']),
'accepted',
message='Accept to %s' % self.api.project)
self.create_new_links(self.api.project, req['package'], oldspecs)
if 'type' in req and req['type'] == 'delete' and self.api.delreq_review:
msg += ' and started handling of virtual accept process'
print(msg)
# Virtually accept the delete request
self.virtually_accept_delete(req['id'], req['package'])
else:
print(msg)
change_request_state(self.api.apiurl,
str(req['id']),
'accepted',
message='Accept to %s' % self.api.project)
self.create_new_links(self.api.project, req['package'], oldspecs)
self.api.accept_status_comment(project, packages)
self.api.staging_deactivate(project)
@ -128,6 +185,16 @@ class AcceptCommand(object):
def accept_other_new(self):
changed = False
if self.api.delreq_review:
rqlist = self.find_virtually_accepted_requests(self.api.project)
for req in rqlist:
if self.virtual_accept_request_has_no_binary(self.api.project, req['packages'][0]):
# Accepting delreq-review review
self.api.do_change_review_state(req['id'], 'accepted',
by_group=self.api.delreq_review,
message='Virtually accepted delete {}'.format(req['packages'][0]))
rqlist = self.find_new_requests(self.api.project)
if self.api.cnonfree:
rqlist += self.find_new_requests(self.api.cnonfree)
@ -135,6 +202,9 @@ class AcceptCommand(object):
for req in rqlist:
oldspecs = self.api.get_filelist_for_package(pkgname=req['packages'][0], project=self.api.project, extension='spec')
print 'Accepting request %d: %s' % (req['id'], ','.join(req['packages']))
if req['type'] == 'delete':
# Remove devel project/package tag before accepting the request
self.remove_obsoleted_develtag(self.api.project, req['packages'][0])
change_request_state(self.api.apiurl, str(req['id']), 'accepted', message='Accept to %s' % self.api.project)
# Check if all .spec files of the package we just accepted has a package container to build
self.create_new_links(self.api.project, req['packages'][0], oldspecs)
@ -142,6 +212,14 @@ class AcceptCommand(object):
return changed
def remove_obsoleted_develtag(self, project, package):
xpath = {
'package': "@project='%s' and devel/@project=@project and devel/@package='%s'" % (project, package),
}
collection = search(self.api.apiurl, **xpath)['package']
for pkg in collection.findall('package'):
set_devel_project(self.api.apiurl, project, pkg.attrib['name'], devprj=None)
def create_new_links(self, project, pkgname, oldspeclist):
filelist = self.api.get_filelist_for_package(pkgname=pkgname, project=project, extension='spec')
removedspecs = set(oldspeclist) - set(filelist)

View File

@ -44,6 +44,8 @@ DEFAULT = {
'openqa': 'https://openqa.opensuse.org',
'lock': 'openSUSE:%(project)s:Staging',
'lock-ns': 'openSUSE',
'delreq-review': 'factory-maintainers',
'main-repo': 'standard',
},
r'openSUSE:(?P<project>Leap:[\d.]+)': {
'staging': 'openSUSE:%(project)s:Staging',

View File

@ -75,6 +75,8 @@ class StagingAPI(object):
self.cproduct = conf.config[project]['product']
self.copenqa = conf.config[project]['openqa']
self.user = conf.get_apiurl_usr(apiurl)
self.delreq_review = conf.config[project]['delreq-review']
self.main_repo = conf.config[project]['main-repo']
self._ring_packages = None
self._ring_packages_for_links = None
self._packages_staged = None
@ -260,6 +262,11 @@ class StagingAPI(object):
return package_info
def extract_specfile_short(self, filelist):
packages = [spec[:-5] for spec in filelist if re.search('\.spec$', spec)]
return packages
def get_filelist_for_package(self, pkgname, project, expand=None, extension=None):
"""
Get a list of files inside a package container
@ -748,7 +755,7 @@ class StagingAPI(object):
def clear_prj_pseudometa(self, project):
self.set_prj_pseudometa(project, {})
def _add_rq_to_prj_pseudometa(self, project, request_id, package):
def _add_rq_to_prj_pseudometa(self, project, request_id, package, act_type=None):
"""
Records request as part of the project within metadata
:param project: project to record into
@ -761,13 +768,14 @@ class StagingAPI(object):
for request in data['requests']:
if request['package'] == package:
# Only update if needed (to save calls to get_request)
if request['id'] != request_id or not request.get('author'):
if request['id'] != request_id or not request.get('author') or not request.get('type'):
request['id'] = request_id
request['type'] = act_type
request['author'] = get_request(self.apiurl, str(request_id)).get_creator()
append = False
if append:
author = get_request(self.apiurl, str(request_id)).get_creator()
data['requests'].append({'id': request_id, 'package': package, 'author': author})
data['requests'].append({'id': request_id, 'package': package, 'author': author, 'type': act_type})
self.set_prj_pseudometa(project, data)
def set_splitter_info_in_prj_pseudometa(self, project, group, strategy_info):
@ -859,24 +867,27 @@ class StagingAPI(object):
self._package_disabled['/'.join([project, package])] = disabled
return disabled
def create_package_container(self, project, package, disable_build=False):
def create_package_container(self, project, package, meta=None, disable_build=False):
"""
Creates a package container without any fields in project/package
:param project: project to create it
:param package: package name
:param meta: package metadata
:param disable_build: should the package be created with build
flag disabled
"""
dst_meta = '<package name="{}"><title/><description/></package>'
dst_meta = dst_meta.format(package)
if not meta:
meta = '<package name="{}"><title/><description/></package>'
meta = meta.format(package)
if disable_build:
root = ET.fromstring(dst_meta)
root = ET.fromstring(meta)
elm = ET.SubElement(root, 'build')
ET.SubElement(elm, 'disable')
dst_meta = ET.tostring(root)
meta = ET.tostring(root)
url = self.makeurl(['source', project, package, '_meta'])
http_PUT(url, data=dst_meta)
http_PUT(url, data=meta)
def check_ring_packages(self, project, requests):
"""
@ -1057,6 +1068,7 @@ class StagingAPI(object):
"""
# read info from sr
tar_pkg = None
act_type = None
req = get_request(self.apiurl, str(request_id))
if not req:
@ -1064,10 +1076,12 @@ class StagingAPI(object):
act = req.get_actions('submit')
if act:
act_type = 'submit'
tar_pkg = self.submit_to_prj(act[0], project)
act = req.get_actions('delete')
if act:
act_type = 'delete'
tar_pkg = self.delete_to_prj(act[0], project)
if not tar_pkg:
@ -1076,7 +1090,7 @@ class StagingAPI(object):
raise oscerr.WrongArgs(msg)
# register the package name
self._add_rq_to_prj_pseudometa(project, int(request_id), tar_pkg)
self._add_rq_to_prj_pseudometa(project, int(request_id), tar_pkg, act_type=act_type)
# add review
self.add_review(request_id, project)