Merge pull request #1573 from coolo/use_attributes
Use attribute to store staging config
This commit is contained in:
commit
67121e297e
87
metrics.py
87
metrics.py
@ -14,6 +14,7 @@ import yaml
|
||||
import metrics_release
|
||||
import osc.conf
|
||||
import osc.core
|
||||
from osc.core import HTTPError
|
||||
from osc.core import get_commitlog
|
||||
import osclib.conf
|
||||
from osclib.cache import Cache
|
||||
@ -351,10 +352,14 @@ def ingest_release_schedule(project):
|
||||
|
||||
def revision_index(api):
|
||||
if not hasattr(revision_index, 'index'):
|
||||
root = ET.fromstring(''.join(
|
||||
get_commitlog(api.apiurl, api.cstaging, 'dashboard', None, format='xml')))
|
||||
|
||||
revision_index.index = {}
|
||||
|
||||
try:
|
||||
root = ET.fromstring(''.join(
|
||||
get_commitlog(api.apiurl, api.cstaging, 'dashboard', None, format='xml')))
|
||||
except HTTPError as e:
|
||||
return revision_index.index
|
||||
|
||||
for logentry in root.findall('logentry'):
|
||||
date = date_parse(logentry.find('date').text)
|
||||
revision_index.index[date] = logentry.get('revision')
|
||||
@ -464,6 +469,74 @@ def ingest_dashboard_revision_get():
|
||||
|
||||
return None
|
||||
|
||||
def ingest_attributes(api):
|
||||
url = api.makeurl(['source', api.project, '_project', '_history'], {'meta': 1})
|
||||
root = ET.parse(osc.core.http_GET(url))
|
||||
|
||||
points = []
|
||||
count = 0
|
||||
revision = 0
|
||||
last_attribute_md5 = ''
|
||||
|
||||
last_values = {}
|
||||
|
||||
for rev in root.findall('./revision'):
|
||||
revision = rev.get('rev')
|
||||
time = datetime.utcfromtimestamp(float(rev.find('./time').text))
|
||||
|
||||
url = api.makeurl(['source', api.project, '_project'],
|
||||
{'meta': 1, 'rev': revision})
|
||||
root = ET.parse(osc.core.http_GET(url))
|
||||
|
||||
attribute = root.find('.//entry[@name="_attribute"]')
|
||||
if attribute is None:
|
||||
continue
|
||||
|
||||
attribute_md5 = attribute.get('md5')
|
||||
if attribute_md5 == last_attribute_md5:
|
||||
continue
|
||||
last_attribute_md5 = attribute_md5
|
||||
|
||||
url = api.makeurl(['source', api.project, '_project', '_attribute'],
|
||||
{'meta': 1, 'rev': revision})
|
||||
root = ET.parse(osc.core.http_GET(url))
|
||||
#print revision, time, ET.tostring(root)
|
||||
|
||||
for v in root.findall('.//attribute[@namespace="OSRT"]'):
|
||||
attribute_name = v.get('name')
|
||||
last_values.setdefault(attribute_name, None)
|
||||
if last_values[attribute_name] == v.find('value').text:
|
||||
continue
|
||||
# TODO: no idea what fields to make
|
||||
fields = {revision: revision }
|
||||
|
||||
points.append({
|
||||
'measurement': 'attribute_osrt_{}'.format(attribute_name),
|
||||
'fields': fields,
|
||||
'time': time,
|
||||
})
|
||||
|
||||
points.append({
|
||||
'measurement': 'project_meta_revision',
|
||||
'fields': {
|
||||
'revision': revision,
|
||||
},
|
||||
'time': time,
|
||||
})
|
||||
|
||||
if len(points) >= 1000:
|
||||
client.write_points(points, 's')
|
||||
count += len(points)
|
||||
points = []
|
||||
|
||||
if len(points):
|
||||
client.write_points(points, 's')
|
||||
count += len(points)
|
||||
|
||||
print('last revision processed: {}'.format(revision))
|
||||
|
||||
return count
|
||||
|
||||
def ingest_dashboard(api):
|
||||
index = revision_index(api)
|
||||
|
||||
@ -478,7 +551,9 @@ def ingest_dashboard(api):
|
||||
|
||||
count = 0
|
||||
points = []
|
||||
max_revision = 0
|
||||
for made, revision in sorted(index.items()):
|
||||
max_revision = revision
|
||||
if not past:
|
||||
if revision == revision_last:
|
||||
past = True
|
||||
@ -516,7 +591,7 @@ def ingest_dashboard(api):
|
||||
client.write_points(points, 's')
|
||||
count += len(points)
|
||||
|
||||
print('last revision processed: {}'.format(revision))
|
||||
print('last revision processed: {}'.format(max_revision))
|
||||
|
||||
return count
|
||||
|
||||
@ -544,9 +619,11 @@ def main(args):
|
||||
Cache.PATTERNS['/source/[^/]+/dashboard/[^/]+\?rev=.*'] = sys.maxint
|
||||
Cache.init()
|
||||
|
||||
Config(args.project)
|
||||
c = Config(args.project)
|
||||
api = StagingAPI(osc.conf.config['apiurl'], args.project)
|
||||
c.apply_remote(api)
|
||||
|
||||
print('attributes: wrote {:,} points'.format(ingest_attributes(api)))
|
||||
print('dashboard: wrote {:,} points'.format(ingest_dashboard(api)))
|
||||
|
||||
global who_workaround_swap, who_workaround_miss
|
||||
|
@ -422,10 +422,11 @@ def do_staging(self, subcmd, opts, *args):
|
||||
if opts.wipe_cache:
|
||||
Cache.delete_all()
|
||||
|
||||
api = StagingAPI(opts.apiurl, opts.project)
|
||||
config.apply_remote(api)
|
||||
|
||||
needed = lock_needed(cmd, opts)
|
||||
with OBSLock(opts.apiurl, opts.project, reason=cmd, needed=needed) as lock:
|
||||
api = StagingAPI(opts.apiurl, opts.project)
|
||||
config.apply_remote(api)
|
||||
|
||||
# call the respective command and parse args by need
|
||||
if cmd == 'check':
|
||||
|
@ -90,6 +90,7 @@ class Cache(object):
|
||||
'/source/([^/]+)/_meta$': TTL_LONG,
|
||||
'/source/([^/]+)/(?:[^/]+)/(?:_meta|_link)$': TTL_LONG,
|
||||
'/source/([^/]+)/dashboard/[^/]+': TTL_LONG,
|
||||
'/source/([^/]+)/_attribute/[^/]+': TTL_MEDIUM,
|
||||
# Handles clearing local cache on package deletes. Lots of queries like
|
||||
# updating project info, comment, and package additions.
|
||||
'/source/([^/]+)/(?:[^/?]+)(?:\?[^/]+)?$': TTL_LONG,
|
||||
|
@ -14,6 +14,8 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from ConfigParser import ConfigParser
|
||||
from collections import OrderedDict
|
||||
import io
|
||||
@ -103,48 +105,6 @@ DEFAULT = {
|
||||
'mail-noreply': 'noreply@opensuse.org',
|
||||
'mail-release-list': 'opensuse-releaseteam@opensuse.org',
|
||||
},
|
||||
r'SUSE:(?P<project>SLE-15.*$)': {
|
||||
'staging': 'SUSE:%(project)s:Staging',
|
||||
'staging-group': 'sle-staging-managers', # '%(project.lower)s-staging',
|
||||
'staging-archs': 'i586 x86_64',
|
||||
'staging-dvd-archs': '',
|
||||
'rings': 'SUSE:%(project)s:Rings',
|
||||
'nonfree': None,
|
||||
'rebuild': None,
|
||||
'product': None,
|
||||
'openqa': None,
|
||||
'lock': 'SUSE:%(project)s:Staging',
|
||||
'lock-ns': 'SUSE',
|
||||
'leaper-override-group': 'sle-release-managers',
|
||||
'delreq-review': None,
|
||||
'main-repo': 'standard',
|
||||
# check_source.py
|
||||
'check-source-in-air-rename-allow': 'True',
|
||||
'repo-checker': 'repo-checker',
|
||||
'repo_checker-package-comment-devel': '',
|
||||
'pkglistgen-archs': 'x86_64',
|
||||
'pkglistgen-ignore-unresolvable': '1',
|
||||
'pkglistgen-ignore-recommended': '1',
|
||||
'pkglistgen-product-family-last': 'SUSE:SLE-11:GA',
|
||||
},
|
||||
r'SUSE:(?P<project>.*$)': {
|
||||
'staging': 'SUSE:%(project)s:Staging',
|
||||
'staging-group': 'sle-staging-managers', # '%(project.lower)s-staging',
|
||||
'staging-archs': 'i586 x86_64',
|
||||
'staging-dvd-archs': '',
|
||||
'nocleanup-packages': 'Test-DVD-x86_64 sles-release',
|
||||
'rings': None,
|
||||
'nonfree': None,
|
||||
'rebuild': None,
|
||||
'product': None,
|
||||
'openqa': None,
|
||||
'lock': 'SUSE:%(project)s:Staging',
|
||||
'lock-ns': 'SUSE',
|
||||
'remote-config': False,
|
||||
'delreq-review': None,
|
||||
'main-repo': 'standard',
|
||||
'priority': '100', # Lower than SLE-15 since less specific.
|
||||
},
|
||||
# Allows devel projects to utilize tools that require config, but not
|
||||
# complete StagingAPI support.
|
||||
r'(?P<project>.*$)': {
|
||||
@ -161,7 +121,6 @@ DEFAULT = {
|
||||
'lock-ns': None,
|
||||
'delreq-review': None,
|
||||
'main-repo': 'openSUSE_Factory',
|
||||
'remote-config': False,
|
||||
'priority': '1000', # Lowest priority as only a fallback.
|
||||
},
|
||||
}
|
||||
@ -244,18 +203,32 @@ class Config(object):
|
||||
else:
|
||||
return defaults
|
||||
|
||||
def _migrate_staging_config(self, api):
|
||||
# first try staging project's dashboard. Can't rely on cstaging as it's
|
||||
# defined by config
|
||||
config = api.load_file_content(self.project + ':Staging', 'dashboard', 'config')
|
||||
if not config:
|
||||
config = api.load_file_content(self.project, 'dashboard', 'config')
|
||||
if not config:
|
||||
return None
|
||||
|
||||
print("Found config in staging dashboard - migrate now [y/n] (y)? ", end='')
|
||||
response = raw_input().lower()
|
||||
if response != '' and response != 'y':
|
||||
return config
|
||||
|
||||
api.attribute_value_save('Config', config)
|
||||
|
||||
def apply_remote(self, api):
|
||||
"""Fetch remote config and re-process (defaults, remote, .oscrc)."""
|
||||
if not conf.config[self.project].get('remote-config', True):
|
||||
return
|
||||
|
||||
config = api.dashboard_content_load('config')
|
||||
config = api.attribute_value_load('Config')
|
||||
if not config:
|
||||
# try the old way
|
||||
config = self._migrate_staging_config(api)
|
||||
if config:
|
||||
cp = ConfigParser()
|
||||
config = '[remote]\n' + config
|
||||
cp.readfp(io.BytesIO(config))
|
||||
self.remote_values = dict(cp.items('remote'))
|
||||
self.populate_conf()
|
||||
elif config is None:
|
||||
# Write empty config to allow for caching.
|
||||
api.dashboard_content_save('config', '')
|
||||
|
@ -74,7 +74,7 @@ class OBSLock(object):
|
||||
return signature
|
||||
|
||||
def _write(self, signature):
|
||||
url = makeurl(self.apiurl, ['source', self.lock, '_attribute', '%s:LockedBy' % self.ns])
|
||||
url = makeurl(self.apiurl, ['source', self.lock, '_attribute'])
|
||||
data = """
|
||||
<attributes>
|
||||
<attribute namespace='%s' name='LockedBy'>
|
||||
|
@ -20,7 +20,12 @@ import dateutil.parser
|
||||
import json
|
||||
import logging
|
||||
import textwrap
|
||||
import urllib2
|
||||
try:
|
||||
from urllib.error import HTTPError, URLError
|
||||
except ImportError:
|
||||
#python 2.x
|
||||
from urllib2 import HTTPError, URLError
|
||||
|
||||
import time
|
||||
import re
|
||||
from lxml import etree as ET
|
||||
@ -154,7 +159,7 @@ class StagingAPI(object):
|
||||
if data is not None:
|
||||
return func(url, data=data)
|
||||
return func(url)
|
||||
except urllib2.HTTPError as e:
|
||||
except HTTPError as e:
|
||||
if 500 <= e.code <= 599:
|
||||
print 'Error {}, retrying {} in {}s'.format(e.code, url, retry_sleep_seconds)
|
||||
time.sleep(retry_sleep_seconds)
|
||||
@ -292,7 +297,7 @@ class StagingAPI(object):
|
||||
content = http_GET(url)
|
||||
for entry in ET.parse(content).getroot().findall('entry'):
|
||||
filelist.append(entry.attrib['name'])
|
||||
except urllib2.HTTPError as err:
|
||||
except HTTPError as err:
|
||||
if err.code == 404:
|
||||
# The package we were supposed to query does not exist
|
||||
# we can pass this up and return the empty filelist
|
||||
@ -389,7 +394,7 @@ class StagingAPI(object):
|
||||
try:
|
||||
url = self.makeurl(['source', prj, '_project', '_frozenlinks'], {'meta': '1'})
|
||||
root = ET.parse(http_GET(url)).getroot()
|
||||
except urllib2.HTTPError as e:
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
return None
|
||||
meta = self.get_prj_pseudometa(prj)
|
||||
@ -470,7 +475,7 @@ class StagingAPI(object):
|
||||
url = makeurl(self.apiurl, ('source', project, package), query=query)
|
||||
try:
|
||||
return ET.parse(http_GET(url)).getroot()
|
||||
except (urllib2.HTTPError, urllib2.URLError):
|
||||
except (HTTPError, URLError):
|
||||
return None
|
||||
|
||||
def source_info_request(self, request):
|
||||
@ -524,7 +529,7 @@ class StagingAPI(object):
|
||||
replace_old = request_old.find('state').get('name') in ['revoked', 'superseded']
|
||||
|
||||
if (request_new.find('action').get('type') == 'delete' and
|
||||
request_old.find('action').get('type') == 'delete'):
|
||||
request_old.find('action').get('type') == 'delete'):
|
||||
# Both delete requests.
|
||||
if replace_old:
|
||||
# Pointless since identical requests, but user desires.
|
||||
@ -534,11 +539,11 @@ class StagingAPI(object):
|
||||
message = 'sr#{} is an identical delete and is already staged'.format(
|
||||
request_old.get('id'))
|
||||
self.do_change_review_state(request_id, 'declined',
|
||||
by_group=self.cstaging_group, message=message)
|
||||
by_group=self.cstaging_group, message=message)
|
||||
return stage_info, True
|
||||
|
||||
if (request_new.find('action').get('type') !=
|
||||
request_old.find('action').get('type')):
|
||||
request_old.find('action').get('type')):
|
||||
# One delete and one submit.
|
||||
if replace_old:
|
||||
if self.ring_packages.get(target_package):
|
||||
@ -554,7 +559,7 @@ class StagingAPI(object):
|
||||
message = 'sr#{} of a different type should be revoked first'.format(
|
||||
request_old.get('id'))
|
||||
self.do_change_review_state(request_id, 'declined',
|
||||
by_group=self.cstaging_group, message=message)
|
||||
by_group=self.cstaging_group, message=message)
|
||||
return stage_info, True
|
||||
|
||||
# If both submits are from different source projects then check
|
||||
@ -578,7 +583,7 @@ class StagingAPI(object):
|
||||
if source_same:
|
||||
# Keep the original request and decline this identical one.
|
||||
self.do_change_review_state(request_id, 'declined',
|
||||
by_group=self.cstaging_group, message=message)
|
||||
by_group=self.cstaging_group, message=message)
|
||||
else:
|
||||
# Ingore the new request pending manual review.
|
||||
IgnoreCommand(self).perform([str(request_id)], message)
|
||||
@ -851,7 +856,8 @@ class StagingAPI(object):
|
||||
if self._supersede:
|
||||
self.is_package_disabled(sub_prj, sub_pkg, store=True)
|
||||
# Skip inner-project links for letter staging
|
||||
if not self.is_adi_project(project) and sub_prj == project: continue
|
||||
if not self.is_adi_project(project) and sub_prj == project:
|
||||
continue
|
||||
delete_package(self.apiurl, sub_prj, sub_pkg, force=True, msg=msg)
|
||||
|
||||
# Delete the main package in the last
|
||||
@ -957,9 +963,9 @@ class StagingAPI(object):
|
||||
# to protect us against control characters
|
||||
import string
|
||||
all_bytes = string.maketrans('', '')
|
||||
remove_bytes = all_bytes[:8] + all_bytes[14:32] # accept tabs and newlines
|
||||
remove_bytes = all_bytes[:8] + all_bytes[14:32] # accept tabs and newlines
|
||||
|
||||
query = {'nostream' : '1', 'start' : '%s' % offset}
|
||||
query = {'nostream': '1', 'start': '%s' % offset}
|
||||
if last:
|
||||
query['last'] = 1
|
||||
log = StringIO()
|
||||
@ -1255,13 +1261,14 @@ class StagingAPI(object):
|
||||
# dynamically generated and static baselibs.conf.
|
||||
baselibs = False if self.is_adi_project(project) else None
|
||||
if baselibs is False and 'baselibs.conf' in str(self.load_file_content(
|
||||
src_prj, src_pkg, '{}.spec'.format(src_pkg), src_rev)):
|
||||
src_prj, src_pkg, '{}.spec'.format(src_pkg), src_rev)):
|
||||
baselibs = True
|
||||
|
||||
for sub_prj, sub_pkg in self.get_sub_packages(tar_pkg, project):
|
||||
sub_prj = self.map_ring_package_to_subject(project, sub_pkg)
|
||||
# Skip inner-project links for letter staging
|
||||
if not self.is_adi_project(project) and sub_prj == project: continue
|
||||
if not self.is_adi_project(project) and sub_prj == project:
|
||||
continue
|
||||
if self._supersede:
|
||||
disable_build = self._package_disabled.get('/'.join([sub_prj, sub_pkg]), False)
|
||||
self.create_package_container(sub_prj, sub_pkg, disable_build=disable_build)
|
||||
@ -1271,7 +1278,7 @@ class StagingAPI(object):
|
||||
http_PUT(url, data=ET.tostring(root))
|
||||
|
||||
if baselibs is False and 'baselibs.conf' in str(self.load_file_content(
|
||||
src_prj, src_pkg, '{}.spec'.format(sub_pkg), src_rev)):
|
||||
src_prj, src_pkg, '{}.spec'.format(sub_pkg), src_rev)):
|
||||
baselibs = True
|
||||
|
||||
if baselibs:
|
||||
@ -1462,7 +1469,7 @@ class StagingAPI(object):
|
||||
url = self.makeurl(['source', project, '_meta'])
|
||||
try:
|
||||
http_GET(url)
|
||||
except urllib2.HTTPError:
|
||||
except HTTPError:
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -1494,7 +1501,7 @@ class StagingAPI(object):
|
||||
url = self.makeurl(['build', project, repository, arch, '_repository', "%s?view=fileinfo" % rpm])
|
||||
try:
|
||||
return ET.parse(http_GET(url)).getroot().find('version').text
|
||||
except urllib2.HTTPError as e:
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
return None
|
||||
raise
|
||||
@ -1530,6 +1537,36 @@ class StagingAPI(object):
|
||||
if content != self.dashboard_content_load(filename):
|
||||
self.dashboard_content_save(filename, content, comment)
|
||||
|
||||
def attribute_value_load(self, attribute):
|
||||
url = self.makeurl(['source', self.project, '_attribute', 'OSRT:' + attribute])
|
||||
try:
|
||||
f = self.retried_GET(url)
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
return None
|
||||
raise e
|
||||
root = ET.parse(f).getroot()
|
||||
root = root.find('./attribute/value')
|
||||
if root is None:
|
||||
return None
|
||||
return root.text
|
||||
|
||||
# to create a new attribute 'type' you need to do some manual step
|
||||
# create a xml file analoge to what
|
||||
# osc api /attribute/OSRT/IgnoredIssues/_meta outputs
|
||||
# you need to think about roles, groups and users that should be
|
||||
# able to write the attribute
|
||||
# after that osc api -T $xml /attribute/OSRT/$NEWATTRIBUTE/_meta
|
||||
# (preferably do this right away for ibs and obs)
|
||||
def attribute_value_save(self, attribute, text):
|
||||
root = ET.fromstring('<attributes><attribute name="" namespace="OSRT">' +
|
||||
'<value/></attribute></attributes>')
|
||||
root.find('./attribute').set('name', attribute)
|
||||
root.find('./attribute/value').text = text
|
||||
# the OBS API of attributes is super strange, you POST updates
|
||||
url = self.makeurl(['source', self.project, '_attribute'])
|
||||
self.retried_POST(url, data=ET.tostring(root))
|
||||
|
||||
def update_status_or_deactivate(self, project, command):
|
||||
meta = self.get_prj_pseudometa(project)
|
||||
if len(meta['requests']) == 0:
|
||||
@ -1573,7 +1610,7 @@ class StagingAPI(object):
|
||||
len(requests_old) - len(requests_common),
|
||||
command
|
||||
))
|
||||
lines.append('') # Blank line.
|
||||
lines.append('') # Blank line.
|
||||
|
||||
requests = []
|
||||
for req in meta['requests']:
|
||||
@ -1594,7 +1631,7 @@ class StagingAPI(object):
|
||||
dashboard_url = '{}/project/staging_projects/{}/{}'.format(
|
||||
self.apiurl, self.project, self.extract_staging_short(project))
|
||||
lines.append('Requests ([dashboard]({})):'.format(dashboard_url))
|
||||
lines.append('') # Blank line.
|
||||
lines.append('') # Blank line.
|
||||
|
||||
requests = meta['requests']
|
||||
|
||||
@ -1686,7 +1723,6 @@ class StagingAPI(object):
|
||||
except:
|
||||
print "could not trigger rebuild for project '%s' package '%s'" % (prj, pkg)
|
||||
|
||||
|
||||
def _candidate_adi_project(self):
|
||||
"""Decide a candidate name for an ADI project."""
|
||||
adi_projects = self.get_adi_projects()
|
||||
|
@ -27,12 +27,16 @@ class TestConfig(unittest.TestCase):
|
||||
self.assertEqual('local', conf.config[PROJECT]['overridden-by-local'])
|
||||
self.assertEqual('remote-indeed', conf.config[PROJECT]['remote-only'])
|
||||
|
||||
def test_remote_none(self):
|
||||
self.api.dashboard_content_save('config', '')
|
||||
self.assertEqual(self.obs.dashboard_counts['config'], 1)
|
||||
self.api.attribute_value_save('Config', 'remote-only = nope')
|
||||
self.config.apply_remote(self.api)
|
||||
|
||||
self.assertEqual('local', conf.config[PROJECT]['overridden-by-local'])
|
||||
self.assertEqual('nope', conf.config[PROJECT]['remote-only'])
|
||||
|
||||
def test_remote_none(self):
|
||||
self.api.attribute_value_save('Config', '')
|
||||
# don't crash
|
||||
self.config.apply_remote(self.api)
|
||||
# Ensure blank file not overridden.
|
||||
self.assertEqual(self.obs.dashboard_counts['config'], 1)
|
||||
|
||||
def test_pattern_order(self):
|
||||
# Add pattern to defaults in order to identify which was matched.
|
||||
|
@ -1,2 +0,0 @@
|
||||
overridden-by-local = remote-nope
|
||||
remote-only = remote-indeed
|
157
tests/obs.py
157
tests/obs.py
@ -14,6 +14,8 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import os
|
||||
import re
|
||||
@ -53,9 +55,11 @@ def router_handler(route_table, method, request, uri, headers):
|
||||
uri_parsed = urlparse.urlparse(uri)
|
||||
for path, fn in route_table:
|
||||
match = False
|
||||
if isinstance(path, basestring) and uri_parsed.path == path:
|
||||
if not isinstance(path, str):
|
||||
print(path.pattern)
|
||||
if isinstance(path, str) and uri_parsed.path == path:
|
||||
match = True
|
||||
elif not isinstance(path, basestring) and path.search(uri_parsed.path):
|
||||
elif not isinstance(path, str) and path.search(uri_parsed.path):
|
||||
match = True
|
||||
if match:
|
||||
return fn(request, uri, headers)
|
||||
@ -141,7 +145,6 @@ class OBS(object):
|
||||
# build the responses. We will try to put responses as XML
|
||||
# templates in the fixture directory.
|
||||
self.dashboard = {}
|
||||
self.dashboard_counts = {}
|
||||
|
||||
self.requests = {
|
||||
'123': {
|
||||
@ -229,7 +232,7 @@ class OBS(object):
|
||||
'project': 'openSUSE:Factory:Staging:C',
|
||||
'title': 'A project ready to be accepted',
|
||||
'description': ('requests:\n- {id: 501, package: apparmor, author: Admin, type: submit}\n'
|
||||
'- {id: 502, package: mariadb, author: Admin, type: submit}'),
|
||||
'- {id: 502, package: mariadb, author: Admin, type: submit}'),
|
||||
},
|
||||
'J': {
|
||||
'project': 'openSUSE:Factory:Staging:J',
|
||||
@ -267,7 +270,14 @@ class OBS(object):
|
||||
},
|
||||
}
|
||||
|
||||
self.lock = None
|
||||
self.attributes = {
|
||||
'openSUSE:Factory': {
|
||||
'OSRT': {
|
||||
'Config': 'overridden-by-local = remote-nope\n'
|
||||
'remote-only = remote-indeed'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.meta = {}
|
||||
|
||||
@ -408,10 +418,10 @@ class OBS(object):
|
||||
response = (200, headers, template.substitute(self.requests[request_id]))
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'REQUEST', uri, response
|
||||
print('REQUEST', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -448,10 +458,10 @@ class OBS(object):
|
||||
response = (200, headers, self._request(request_id))
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'REVIEW REQUEST', uri, response
|
||||
print('REVIEW REQUEST', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -479,10 +489,10 @@ class OBS(object):
|
||||
response = (200, headers, result)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SEARCH REQUEST', uri, response
|
||||
print('SEARCH REQUEST', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -490,30 +500,52 @@ class OBS(object):
|
||||
# /source/
|
||||
#
|
||||
|
||||
@GET(re.compile(r'/source/openSUSE:Factory:Staging/_attribute/openSUSE:LockedBy'))
|
||||
def source_staging_lock(self, request, uri, headers):
|
||||
"""Return staging lock."""
|
||||
response = (404, headers, '<result>Not found</result>')
|
||||
try:
|
||||
lock = self.lock if self.lock else self._fixture(uri)
|
||||
response = (200, headers, lock)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
@GET(re.compile(r'/source/[\w:]+/_attribute/[\w:]+$'))
|
||||
def source_attribute(self, request, uri, headers):
|
||||
"""Return attribute."""
|
||||
|
||||
match = re.search(r'/source/([\w:]+)/_attribute/(\w+):(\w+)$', uri)
|
||||
project = match.group(1)
|
||||
ns = match.group(2)
|
||||
name = match.group(3)
|
||||
|
||||
# never returns 404 - unless the project does not exist,
|
||||
# which we can't (and don't need) to model
|
||||
response = (200, headers, '<attributes/>')
|
||||
print(self.attributes, project, ns, name)
|
||||
if not project in self.attributes:
|
||||
return response
|
||||
hash = self.attributes[project]
|
||||
|
||||
if not ns in hash and not name in hash[ns]:
|
||||
return response
|
||||
|
||||
root = ET.fromstring('<attributes><attribute name="" namespace="">' +
|
||||
'<value/></attribute></attributes>')
|
||||
root.find('./attribute').set('name', name)
|
||||
root.find('./attribute').set('namespace', ns)
|
||||
root.find('./attribute/value').text = hash[ns][name]
|
||||
|
||||
response = (200, headers, ET.tostring(root))
|
||||
|
||||
if DEBUG:
|
||||
print 'STAGING LOCK', uri, response
|
||||
print('GET ATTRIBUTE', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@POST(re.compile(r'/source/openSUSE:Factory:Staging/_attribute/openSUSE:LockedBy'))
|
||||
def source_staging_lock_put(self, request, uri, headers):
|
||||
"""Set the staging lock."""
|
||||
self.lock = request.body
|
||||
response = (200, headers, self.lock)
|
||||
@POST(re.compile(r'/source/[\w:]+/_attribute$'))
|
||||
def source_attribute_post(self, request, uri, headers):
|
||||
"""Set an attribute"""
|
||||
project = re.search(r'/source/([\w:]+)/_attribute', uri).group(1)
|
||||
_attrib = ET.fromstring(request.body).find('./attribute')
|
||||
hash = self.attributes.setdefault(project, {})
|
||||
hash = hash.setdefault(_attrib.get('namespace'), {})
|
||||
text = _attrib.find('./value').text
|
||||
hash[_attrib.get('name')] = text or ''
|
||||
response = (200, headers, request.body)
|
||||
|
||||
if DEBUG:
|
||||
print 'PUT STAGING LOCK', uri, response
|
||||
print('PUT ATTRIBUTE', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -527,10 +559,10 @@ class OBS(object):
|
||||
response = (200, headers, contents)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'STAGING DASHBOARD FILE', uri, response
|
||||
print('STAGING DASHBOARD FILE', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -539,11 +571,10 @@ class OBS(object):
|
||||
"""Set the staging dashboard file contents."""
|
||||
filename = re.search(r'/source/[\w:]+/\w+/(\w+)', uri).group(1)
|
||||
self.dashboard[filename] = request.body
|
||||
self.dashboard_counts[filename] = self.dashboard_counts.get(filename, 0) + 1
|
||||
response = (200, headers, request.body)
|
||||
|
||||
if DEBUG:
|
||||
print 'PUT STAGING DASHBOARD FILE', uri, response
|
||||
print('PUT STAGING DASHBOARD FILE', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -565,10 +596,10 @@ class OBS(object):
|
||||
response = (200, headers, history)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'STAGING PROJECT _history META', uri, response
|
||||
print('STAGING PROJECT _history META', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -592,10 +623,10 @@ class OBS(object):
|
||||
response = (200, headers, meta)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'STAGING PROJECT _project _meta', uri, response
|
||||
print('STAGING PROJECT _project _meta', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -614,10 +645,10 @@ class OBS(object):
|
||||
response = (200, headers, project)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'STAGING PROJECT _PROJECT', uri, response
|
||||
print('STAGING PROJECT _PROJECT', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -636,10 +667,10 @@ class OBS(object):
|
||||
response = (200, headers, meta)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'STAGING PROJECT [PACKAGE] _META', uri, response
|
||||
print('STAGING PROJECT [PACKAGE] _META', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -654,7 +685,7 @@ class OBS(object):
|
||||
response = (200, headers, meta)
|
||||
|
||||
if DEBUG:
|
||||
print 'PUT STAGING PROJECT [PACKAGE] _META', uri, response
|
||||
print('PUT STAGING PROJECT [PACKAGE] _META', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -668,10 +699,10 @@ class OBS(object):
|
||||
response = (200, headers, template.substitute(self.links[package]))
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SOURCE WINE', uri, response
|
||||
print('SOURCE WINE', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -685,10 +716,10 @@ class OBS(object):
|
||||
response = (200, headers, '<result>Ok</result>')
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'DELETE', uri, response
|
||||
print('DELETE', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -709,10 +740,10 @@ class OBS(object):
|
||||
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SOURCE APPARMOR', uri, response
|
||||
print('SOURCE APPARMOR', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -732,10 +763,10 @@ class OBS(object):
|
||||
response = (200, headers, template.substitute(self.package[index]))
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SOURCE HOME:ADMIN', package, uri, response
|
||||
print('SOURCE HOME:ADMIN', package, uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -753,10 +784,10 @@ class OBS(object):
|
||||
response = (200, headers, template.substitute(self.links[project_package]))
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SOURCE HOME:ADMIN WINE', uri, response
|
||||
print('SOURCE HOME:ADMIN WINE', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -777,10 +808,10 @@ class OBS(object):
|
||||
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'PUT SOURCE WINE _LINK', uri, response
|
||||
print('PUT SOURCE WINE _LINK', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -828,10 +859,10 @@ class OBS(object):
|
||||
response = (200, headers, result)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SEARCH PROJECT ID', uri, response
|
||||
print('SEARCH PROJECT ID', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -867,10 +898,10 @@ class OBS(object):
|
||||
response = (200, headers, result)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SEARCH REQUEST', uri, response
|
||||
print('SEARCH REQUEST', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -900,10 +931,10 @@ class OBS(object):
|
||||
response = (200, headers, result)
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'SEARCH REQUEST', uri, response
|
||||
print('SEARCH REQUEST', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -968,10 +999,10 @@ class OBS(object):
|
||||
}))
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'REQUEST', uri, response
|
||||
print('REQUEST', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
@ -987,10 +1018,10 @@ class OBS(object):
|
||||
response = (200, headers, self._fixture(uri))
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print uri, e
|
||||
print(uri, e)
|
||||
|
||||
if DEBUG:
|
||||
print 'DEFAULT', uri, response
|
||||
print('DEFAULT', uri, response)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -71,12 +71,9 @@ class ToTestBase(object):
|
||||
self.amqp_url = osc.conf.config.get('ttm_amqp_url')
|
||||
|
||||
def load_issues_to_ignore(self):
|
||||
url = self.api.makeurl(['source', self.project, '_attribute', 'OSRT:IgnoredIssues'])
|
||||
f = self.api.retried_GET(url)
|
||||
root = ET.parse(f).getroot()
|
||||
root = root.find('./attribute/value')
|
||||
if root is not None:
|
||||
root = yaml.load(root.text)
|
||||
text = self.api.attribute_value_load('IgnoredIssues')
|
||||
if text:
|
||||
root = yaml.load(text)
|
||||
self.issues_to_ignore = root.get('last_seen')
|
||||
else:
|
||||
self.issues_to_ignore = dict()
|
||||
@ -85,11 +82,7 @@ class ToTestBase(object):
|
||||
if self.dryrun:
|
||||
return
|
||||
text = yaml.dump({'last_seen': self.issues_to_ignore}, default_flow_style=False)
|
||||
root = ET.fromstring('<attributes><attribute name="IgnoredIssues" namespace="OSRT">' +
|
||||
'<value/></attribute></attributes>')
|
||||
root.find('./attribute/value').text = text
|
||||
url = self.api.makeurl(['source', self.project, '_attribute'])
|
||||
self.api.retried_POST(url, data=ET.tostring(root))
|
||||
self.api.attribute_value_save('IgnoredIssues', text)
|
||||
|
||||
def openqa_group(self):
|
||||
return self.project
|
||||
|
Loading…
x
Reference in New Issue
Block a user