diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2012-09-25 18:33:25 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2012-09-25 18:33:25 (GMT) |
commit | 18af0e0bc89982ccfe03d502a0730bc13099503d (patch) | |
tree | 95caa01848effe21ab8d48a25638d0360019e9f0 | |
parent | e1fc93cebba27782ec6341504207ee968b8acae8 (diff) |
Simplify access to feeds by using regular properties instead of blobs; populate feed content basing on aliases
-rw-r--r-- | sugar_network/local/mounts.py | 39 | ||||
-rw-r--r-- | sugar_network/local/mountset.py | 22 | ||||
-rw-r--r-- | sugar_network/node/obs.py | 88 | ||||
-rw-r--r-- | sugar_network/node/router.py | 31 | ||||
-rw-r--r-- | sugar_network/resources/context.py | 98 | ||||
-rw-r--r-- | sugar_network/resources/volume.py | 1 | ||||
-rw-r--r-- | sugar_network/toolkit/router.py | 18 | ||||
-rw-r--r-- | sugar_network/zerosugar/feeds.py | 20 | ||||
-rw-r--r-- | tests/__init__.py | 17 | ||||
-rw-r--r-- | tests/units/__main__.py | 2 | ||||
-rwxr-xr-x | tests/units/context.py | 177 | ||||
-rwxr-xr-x | tests/units/home_mount.py | 48 | ||||
-rwxr-xr-x | tests/units/injector.py | 159 | ||||
-rwxr-xr-x | tests/units/node.py | 4 | ||||
-rwxr-xr-x | tests/units/node_mount.py | 34 | ||||
-rwxr-xr-x | tests/units/obs.py | 168 | ||||
-rwxr-xr-x | tests/units/remote_mount.py | 2 |
17 files changed, 673 insertions, 255 deletions
diff --git a/sugar_network/local/mounts.py b/sugar_network/local/mounts.py index 6bcc813..3203c3b 100644 --- a/sugar_network/local/mounts.py +++ b/sugar_network/local/mounts.py @@ -20,7 +20,7 @@ from os.path import isabs, exists, join, basename, isdir from gettext import gettext as _ import active_document as ad -from sugar_network.zerosugar import Bundle, Spec +from sugar_network.zerosugar import Bundle from sugar_network.local import activities, cache from sugar_network.resources.volume import Request from sugar_network import local, checkin, sugar, Client @@ -116,48 +116,13 @@ class HomeMount(LocalMount): @ad.property_command(method='GET', cmd='get_blob') def get_blob(self, document, guid, prop, request=None): - if document == 'context' and prop == 'feed': - return self._get_feed(request) - elif document == 'implementation' and prop == 'data': + if document == 'implementation' and prop == 'data': path = activities.guid_to_path(guid) if exists(path): return {'path': path} else: return LocalMount.get_blob(self, document, guid, prop, request) - def _get_feed(self, request): - versions = {} - - for path in activities.checkins(request['guid']): - try: - spec = Spec(root=path) - except Exception: - util.exception(_logger, 'Failed to read %r spec file', path) - continue - - if request.access_level == ad.ACCESS_LOCAL: - impl_id = spec.root - else: - impl_id = activities.path_to_guid(spec.root) - - versions[spec['version']] = { - '*-*': { - 'guid': impl_id, - 'stability': 'stable', - 'commands': { - 'activity': { - 'exec': spec['Activity', 'exec'], - }, - }, - 'requires': spec.requires, - }, - } - - if not versions: - return None - else: - return {'versions': versions} - def _events_cb(self, event): found_commons = False props = event.get('props') diff --git a/sugar_network/local/mountset.py b/sugar_network/local/mountset.py index 5625ecb..111e83c 100644 --- a/sugar_network/local/mountset.py +++ b/sugar_network/local/mountset.py @@ -112,29 +112,9 @@ class Mountset(dict, ad.CommandsProcessor, Commands, SyncCommands): content={'keep': True}), ad.Response()) - @ad.volume_command(method='GET', cmd='requires') - def requires(self, mountpoint, context): - mount = self.get(mountpoint) - enforce(mount is not None, 'No such mountpoint') - mount.mounted.wait() - - requires = set() - - for guid in [context] if isinstance(context, basestring) else context: - feed = mount.call( - Request(method='GET', document='context', guid=guid, - prop='feed', accept_language=[self._lang]), - ad.Response()) - for impls in feed.values(): - for impl in impls.values(): - if 'requires' in impl: - requires.update(impl['requires'].keys()) - - return list(requires) - def super_call(self, request, response): if 'mountpoint' in request: - mountpoint = request.pop('mountpoint') + mountpoint = request.mountpoint = request.pop('mountpoint') else: mountpoint = '/' mount = self[mountpoint] diff --git a/sugar_network/node/obs.py b/sugar_network/node/obs.py index db85997..0252357 100644 --- a/sugar_network/node/obs.py +++ b/sugar_network/node/obs.py @@ -26,6 +26,10 @@ obs_url = Option( 'for master server', default='https://obs.sugarlabs.org') +obs_project = Option( + 'OBS project to use unattended building', + default='base') + obs_presolve_project = Option( 'OBS project to use with packagekit-backend-presolve', default='resolve') @@ -33,13 +37,80 @@ obs_presolve_project = Option( _logger = logging.getLogger('node.obs') _client = None +_repos = None +_presolve_repos = None + + +def get_repos(): + global _repos + + if _repos is None: + _repos = [] + repos = _request('GET', ['build', obs_project.value]) + for repo in repos.findall('entry'): + repo = repo.get('name') + if '-' not in repo: + continue + arches = _request('GET', ['build', obs_project.value, repo]) + _repos.append({ + 'distributor_id': repo.split('-', 1)[0], + 'name': repo, + 'arches': [i.get('name') for i in arches.findall('entry')], + }) + + return _repos def get_presolve_repos(): + global _presolve_repos + + if _presolve_repos is None: + _presolve_repos = [] + repos = _request('GET', ['build', obs_presolve_project.value]) + for repo in repos.findall('entry'): + repo = repo.get('name') + arches = _request('GET', + ['build', obs_presolve_project.value, repo]) + for arch in arches.findall('entry'): + _presolve_repos.append({ + 'name': repo, + 'arch': arch.get('name'), + }) + + return _presolve_repos + + +def resolve(repo, arch, names): + for package in names: + _request('GET', ['resolve'], params={ + 'project': obs_project.value, + 'repository': repo, + 'arch': arch, + 'package': package, + }) + + +def presolve(repo, arch, names): result = [] - reply = _request('GET', ['build', obs_presolve_project.value]) - for i in reply.findall('entry'): - result.append(i.get('name')) + + for package in names: + reply = _request('GET', ['resolve'], params={ + 'project': obs_presolve_project.value, + 'repository': repo, + 'arch': arch, + 'package': package, + 'withdeps': '1', + # TODO exclude package might be different on different platforms + 'exclude': 'sugar', + }) + for pkg in reply.findall('binary'): + result.append({ + # TODO more distros after supporting them PK backend + 'distributor_id': 'Fedora', + 'name': pkg.get('name'), + 'url': pkg.get('url'), + }) + return result @@ -49,8 +120,15 @@ def _request(*args, **kwargs): if _client is None: _client = http.Client(obs_url.value) - response = _client.request(*args, **kwargs) + response = _client.request(*args, allowed=(400, 404), **kwargs) enforce(response.headers.get('Content-Type') == 'text/xml', 'Irregular OBS response') # pylint: disable-msg=E1103 - return ElementTree.parse(response.raw).getroot() + reply = ElementTree.parse(response.raw).getroot() + + if response.status_code != 200: + summary = reply.find('summary') + enforce(summary is not None, 'Unknown OBS error') + raise RuntimeError(summary.text) + + return reply diff --git a/sugar_network/node/router.py b/sugar_network/node/router.py index da6f131..44aaf25 100644 --- a/sugar_network/node/router.py +++ b/sugar_network/node/router.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import json import logging import active_document as ad @@ -27,10 +26,6 @@ _logger = logging.getLogger('node.router') class Router(router.Router): - def __init__(self, commands): - router.Router.__init__(self, commands) - self._repos = None - @router.route('GET', '/packages') def packages(self, request, response): response.content_type = 'application/json' @@ -49,13 +44,12 @@ class Router(router.Router): self._get_package(request.path[1], request.path[2]) def _list_repos(self): - if self._repos is None: - if self.commands.is_master: - # Node should not depend on OBS - self._repos = obs.get_presolve_repos() - else: - self._repos = [] - return {'total': len(self._repos), 'result': self._repos} + if self.commands.is_master: + # Node should not depend on OBS + repos = obs.get_presolve_repos() + else: + repos = [] + return {'total': len(repos), 'result': repos} def _list_packages(self, request): directory = self.commands.volume['context'] @@ -66,12 +60,9 @@ class Router(router.Router): def _get_package(self, repo, package): directory = self.commands.volume['context'] context = directory.get(package) - meta = context.meta('feed') - enforce(meta is not None and 'path' in meta, ad.NotFound, 'No feed') - - with file(meta['path']) as f: - feed = json.load(f) - enforce('presolve' in feed and repo in feed['presolve'], ad.NotFound, + enforce('package' in context.get('type'), ad.NotFound, + 'Is not a package') + presolve = context.get('presolve', {}).get(repo) + enforce(presolve and 'binary' in presolve, ad.NotFound, 'No presolve info') - - return feed['presolve'][repo] + return presolve['binary'] diff --git a/sugar_network/resources/context.py b/sugar_network/resources/context.py index 36cec2d..4a02e90 100644 --- a/sugar_network/resources/context.py +++ b/sugar_network/resources/context.py @@ -16,9 +16,12 @@ from os.path import join import active_document as ad - from sugar_network import resources, static +from sugar_network.local import activities from sugar_network.resources.volume import Resource +from sugar_network.zerosugar import Spec +from sugar_network.node import obs +from active_toolkit import coroutine, util class Context(Resource): @@ -87,11 +90,6 @@ class Context(Resource): def preview(self, value): return value - @ad.active_property(ad.BlobProperty, - permissions=ad.ACCESS_READ, mime_type='application/json') - def feed(self, value): - return value - @ad.active_property(prefix='K', typecast=bool, default=False) def keep(self, value): return value @@ -104,6 +102,57 @@ class Context(Resource): def position(self, value): return value + @ad.active_property(ad.StoredProperty, typecast=dict, default={}) + def versions(self, value): + if self.request.mountpoint != '~': + return value + + versions = {} + + for path in activities.checkins(self.guid): + try: + spec = Spec(root=path) + except Exception: + util.exception('Failed to read %r spec file', path) + continue + + if self.request.access_level == ad.ACCESS_LOCAL: + impl_id = spec.root + else: + impl_id = activities.path_to_guid(spec.root) + + versions[spec['version']] = { + '*-*': { + 'guid': impl_id, + 'stability': 'stable', + 'commands': { + 'activity': { + 'exec': spec['Activity', 'exec'], + }, + }, + 'requires': spec.requires, + }, + } + + return versions + + @ad.active_property(ad.StoredProperty, typecast=dict, default={}) + def aliases(self, value): + return value + + @aliases.setter + def aliases(self, value): + coroutine.spawn(self._process_aliases, value) + return value + + @ad.active_property(ad.StoredProperty, typecast=dict, default={}) + def packages(self, value): + return value + + @ad.active_property(ad.StoredProperty, typecast=dict, default={}) + def presolve(self, value): + return value + @classmethod @ad.directory_command(method='PUT', cmd='include', arguments={'layers': ad.to_list}) @@ -117,3 +166,40 @@ class Context(Resource): def exclude(cls, directory, layers, request): import logging logging.error('exclude> %r %r', layers, request.content) + + def _process_aliases(self, aliases): + packages = {} + for repo in obs.get_repos(): + alias = aliases.get(repo['distributor_id']) + if not alias or '*' not in alias: + continue + alias = alias['*'].copy() + try: + to_resolve = alias.get('binary', []) + \ + alias.get('devel', []) + for arch in repo['arches']: + obs.resolve(repo['name'], arch, to_resolve) + alias['status'] = 'success' + except Exception, error: + util.exception('Failed to resolve %r', alias) + alias = {'status': str(error)} + packages[repo['name']] = alias + + presolve = {} + for repo in obs.get_presolve_repos(): + alias = aliases.get(repo['distributor_id']) + if not alias or '*' not in alias: + continue + alias = alias['*'].copy() + try: + for key, names in alias.items(): + alias[key] = \ + obs.presolve(repo['name'], repo['arch'], names) + alias['status'] = 'success' + except Exception, error: + util.exception('Failed to preresolve %r', alias) + alias = {'status': str(error)} + presolve[repo['name']] = alias + + self.request.call('PUT', document='context', guid=self.guid, + content={'packages': packages, 'presolve': presolve}) diff --git a/sugar_network/resources/volume.py b/sugar_network/resources/volume.py index 975330e..c949bb1 100644 --- a/sugar_network/resources/volume.py +++ b/sugar_network/resources/volume.py @@ -33,6 +33,7 @@ _logger = logging.getLogger('resources.volume') class Request(ad.Request): blobs = None + mountpoint = None class Resource(ad.Document): diff --git a/sugar_network/toolkit/router.py b/sugar_network/toolkit/router.py index 23890ed..58478c7 100644 --- a/sugar_network/toolkit/router.py +++ b/sugar_network/toolkit/router.py @@ -235,9 +235,17 @@ class Router(object): class _Request(Request): - def __init__(self, environ): + environ = None + url = None + path = None + principal = None + + def __init__(self, environ=None): Request.__init__(self) + if not environ: + return + self.access_level = ad.ACCESS_REMOTE self.environ = environ self.url = '/' + environ['PATH_INFO'].strip('/') @@ -287,6 +295,14 @@ class _Request(Request): elif scope == 1: self['document'], = self.path + def clone(self): + request = Request.clone(self) + request.environ = self.environ + request.url = self.url + request.path = self.path + request.principal = self.principal + return request + class _Response(ad.Response): diff --git a/sugar_network/zerosugar/feeds.py b/sugar_network/zerosugar/feeds.py index 675d4ac..8141434 100644 --- a/sugar_network/zerosugar/feeds.py +++ b/sugar_network/zerosugar/feeds.py @@ -13,14 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import json import logging from os.path import isabs from zeroinstall.injector import model from sugar_network.zerosugar import lsb_release, parse_version -from active_toolkit import util, enforce +from active_toolkit import util clients = [] @@ -35,14 +34,9 @@ def read(context): client = None for client in clients: try: - blob = client.get(['context', context, 'feed'], cmd='get_blob') - enforce(blob is not None, 'Feed not found') - if 'path' in blob: - with file(blob['path']) as f: - feed_content = json.load(f) - else: - feed_content = blob - _logger.debug('Found %r feed in %r mountpoint', + feed_content = client.get(['context', context], + reply=['versions', 'packages']) + _logger.debug('Found %r in %r mountpoint', context, client.params['mountpoint']) break except Exception: @@ -54,13 +48,11 @@ def read(context): _logger.warning('No feed for %r context', context) return None - packages = feed_content.get('packages') or {} - distro = packages.get(lsb_release.distributor_id()) + distro = feed_content['packages'].get(lsb_release.distributor_id()) if distro: feed.to_resolve = distro.get('binary') - versions = feed_content.get('versions') or {} - for version, version_data in versions.items(): + for version, version_data in feed_content['versions'].items(): for arch, impl_data in version_data.items(): impl_id = impl_data['guid'] diff --git a/tests/__init__.py b/tests/__init__.py index edec1e0..2ec7f1d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -25,8 +25,9 @@ from sugar_network.local.mountset import Mountset from sugar_network import local, node from sugar_network.resources.user import User from sugar_network.resources.context import Context -from sugar_network.node.commands import NodeCommands -from sugar_network.node import stats +from sugar_network.node.commands import NodeCommands, MasterCommands +from sugar_network.node.router import Router as MasterRouter +from sugar_network.node import stats, obs from sugar_network.resources.volume import Volume @@ -87,6 +88,7 @@ class Test(unittest.TestCase): stats.stats_step.value = 1 stats.stats_rras.value = ['RRA:AVERAGE:0.5:1:100'] stats._cache.clear() + obs._client = None Volume.RESOURCES = [ 'sugar_network.resources.user', @@ -266,6 +268,17 @@ class Test(unittest.TestCase): httpd.stop() volume.close() + def start_master(self, classes=None): + if classes is None: + classes = [User, Context] + self.touch('master/master') + volume = Volume('master', classes) + cp = MasterCommands(volume) + self.server = coroutine.WSGIServer(('localhost', 8800), MasterRouter(cp)) + coroutine.spawn(self.server.serve_forever) + coroutine.dispatch() + return volume + def sign(privkey, data): with tempfile.NamedTemporaryFile() as tmp_privkey: diff --git a/tests/units/__main__.py b/tests/units/__main__.py index 5ecbaa6..19c72b7 100644 --- a/tests/units/__main__.py +++ b/tests/units/__main__.py @@ -22,5 +22,7 @@ from remote_mount import * from node_mount import * from injector import * from mountset import * +from context import * +from obs import * tests.main() diff --git a/tests/units/context.py b/tests/units/context.py new file mode 100755 index 0000000..e9ea911 --- /dev/null +++ b/tests/units/context.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# sugar-lint: disable + +from __init__ import tests + +from sugar_network.node import obs +from sugar_network import IPCClient, Client +from active_toolkit import coroutine, enforce + + +class ContextTest(tests.Test): + + def test_Aliases(self): + self.override(obs, 'get_repos', lambda: [ + {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, + {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arches': ['x86']}, + {'distributor_id': 'Debian', 'name': 'Debian-7.0', 'arches': ['x86_64']}, + ]) + self.override(obs, 'get_presolve_repos', lambda: [ + {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arch': 'x86'}, + {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arch': 'x86'}, + {'distributor_id': 'Debian', 'name': 'Debian-7.0', 'arch': 'x86_64'}, + ]) + self.override(obs, 'resolve', lambda repo, arch, names: ['fake']) + self.override(obs, 'presolve', lambda repo, arch, names: ['%s-%s-%s' % (repo, arch, i) for i in names]) + + self.start_server() + client = IPCClient(mountpoint='~') + + guid = client.post(['context'], { + 'type': 'activity', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + + client.put(['context', guid, 'aliases'], { + 'Gentoo': { + '*': { + 'binary': ['pkg1.bin', 'pkg2.bin'], + 'devel': ['pkg3.devel'], + }, + }, + 'Debian': { + '*': { + 'binary': ['pkg4.bin'], + 'devel': ['pkg5.devel', 'pkg6.devel'], + }, + }, + }) + coroutine.dispatch() + self.assertEqual({ + 'Gentoo-2.1': {'status': 'success', 'binary': ['pkg1.bin', 'pkg2.bin'], 'devel': ['pkg3.devel']}, + 'Debian-6.0': {'status': 'success', 'binary': ['pkg4.bin'], 'devel': ['pkg5.devel', 'pkg6.devel']}, + 'Debian-7.0': {'status': 'success', 'binary': ['pkg4.bin'], 'devel': ['pkg5.devel', 'pkg6.devel']}, + }, + client.get(['context', guid, 'packages'])) + self.assertEqual({ + 'Gentoo-2.1': {'status': 'success', 'binary': ['Gentoo-2.1-x86-pkg1.bin', 'Gentoo-2.1-x86-pkg2.bin'], 'devel': ['Gentoo-2.1-x86-pkg3.devel']}, + 'Debian-6.0': {'status': 'success', 'binary': ['Debian-6.0-x86-pkg4.bin'], 'devel': ['Debian-6.0-x86-pkg5.devel', 'Debian-6.0-x86-pkg6.devel']}, + 'Debian-7.0': {'status': 'success', 'binary': ['Debian-7.0-x86_64-pkg4.bin'], 'devel': ['Debian-7.0-x86_64-pkg5.devel', 'Debian-7.0-x86_64-pkg6.devel']}, + }, + client.get(['context', guid, 'presolve'])) + + def test_WrongAliases(self): + self.override(obs, 'get_repos', lambda: [ + {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, + {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arches': ['x86']}, + {'distributor_id': 'Debian', 'name': 'Debian-7.0', 'arches': ['x86_64']}, + ]) + self.override(obs, 'get_presolve_repos', lambda: [ + {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arch': 'x86'}, + {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arch': 'x86'}, + {'distributor_id': 'Debian', 'name': 'Debian-7.0', 'arch': 'x86_64'}, + ]) + self.override(obs, 'resolve', lambda repo, arch, names: enforce(False, 'resolve failed')) + self.override(obs, 'presolve', lambda repo, arch, names: enforce(False, 'presolve failed')) + + self.start_server() + client = IPCClient(mountpoint='~') + + guid = client.post(['context'], { + 'type': 'activity', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + + client.put(['context', guid, 'aliases'], { + 'Gentoo': { + '*': { + 'binary': ['pkg1.bin', 'pkg2.bin'], + 'devel': ['pkg3.devel'], + }, + }, + 'Debian': { + '*': { + 'binary': ['pkg4.bin'], + 'devel': ['pkg5.devel', 'pkg6.devel'], + }, + }, + }) + coroutine.dispatch() + self.assertEqual({ + 'Gentoo-2.1': {'status': 'resolve failed'}, + 'Debian-6.0': {'status': 'resolve failed'}, + 'Debian-7.0': {'status': 'resolve failed'}, + }, + client.get(['context', guid, 'packages'])) + self.assertEqual({ + 'Gentoo-2.1': {'status': 'presolve failed'}, + 'Debian-6.0': {'status': 'presolve failed'}, + 'Debian-7.0': {'status': 'presolve failed'}, + }, + client.get(['context', guid, 'presolve'])) + + def test_PackagesRoute(self): + self.override(obs, 'get_presolve_repos', lambda: [ + {'name': 'Gentoo-2.1', 'arch': 'x86'}, + {'name': 'Debian-6.0', 'arch': 'x86_64'}, + ]) + + volume = self.start_master() + client = Client() + + client.post(['context'], { + 'type': 'package', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + 'implement': 'package1', + 'presolve': { + 'Gentoo-2.1': {'status': 'success', 'binary': ['package1-1', 'package1-2']}, + 'Debian-6.0': {'status': 'success', 'binary': ['package1-3']}, + }, + }) + client.post(['context'], { + 'type': 'package', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + 'implement': 'package2', + 'presolve': { + 'Gentoo-2.1': {'status': 'success', 'devel': ['package2-1', 'package2-2']}, + 'Debian-6.0': {'status': 'success', 'devel': ['package2-3']}, + }, + }) + client.post(['context'], { + 'type': 'activity', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + 'implement': 'package3', + 'presolve': { + 'Gentoo-2.1': {'status': 'success', 'binary': ['package3-1', 'package3-2']}, + 'Debian-6.0': {'status': 'success', 'binary': ['package3-3']}, + }, + }) + + self.assertEqual( + {'total': 2, 'result': [{'arch': 'x86', 'name': 'Gentoo-2.1'}, {'arch': 'x86_64', 'name': 'Debian-6.0'}]}, + client.request('GET', ['packages'])) + self.assertEqual( + {'total': 2, 'result': ['package1', 'package2']}, + client.request('GET', ['packages', 'Gentoo-2.1'])) + self.assertEqual( + ['package1-1', 'package1-2'], + client.request('GET', ['packages', 'Gentoo-2.1', 'package1'])) + self.assertEqual( + ['package1-3'], + client.request('GET', ['packages', 'Debian-6.0', 'package1'])) + self.assertRaises(RuntimeError, client.request, 'GET', ['packages', 'Debian-6.0', 'package2']) + self.assertRaises(RuntimeError, client.request, 'GET', ['packages', 'Gentoo-2.1', 'package3']) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/home_mount.py b/tests/units/home_mount.py index dc339d5..a632390 100755 --- a/tests/units/home_mount.py +++ b/tests/units/home_mount.py @@ -202,39 +202,37 @@ class HomeMountTest(tests.Test): coroutine.sleep() self.assertEqual({ - 'versions': { - '1': { - '*-*': { - 'commands': { - 'activity': { - 'exec': 'false', - }, + '1': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'false', }, - 'stability': 'stable', - 'guid': tests.tmpdir + '/Activities/activity-1', - 'requires': {}, }, + 'stability': 'stable', + 'guid': tests.tmpdir + '/Activities/activity-1', + 'requires': {}, }, - '2': { - '*-*': { - 'commands': { - 'activity': { - 'exec': 'true', - }, - }, - 'stability': 'stable', - 'guid': tests.tmpdir + '/Activities/activity-2', - 'requires': { - 'dep1': {}, - 'dep2': {'restrictions': [['1', '2']]}, - 'dep3': {'restrictions': [[None, '2']]}, - 'dep4': {'restrictions': [['3', None]]}, + }, + '2': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'true', }, }, + 'stability': 'stable', + 'guid': tests.tmpdir + '/Activities/activity-2', + 'requires': { + 'dep1': {}, + 'dep2': {'restrictions': [['1', '2']]}, + 'dep3': {'restrictions': [[None, '2']]}, + 'dep4': {'restrictions': [['3', None]]}, + }, }, }, }, - client.get(['context', 'bundle_id', 'feed'], cmd='get_blob')) + client.get(['context', 'bundle_id', 'versions'])) if __name__ == '__main__': diff --git a/tests/units/injector.py b/tests/units/injector.py index 0c36459..21333da 100755 --- a/tests/units/injector.py +++ b/tests/units/injector.py @@ -33,12 +33,6 @@ class InjectorTest(tests.Test): 'description': 'description', }) - blob_path = 'remote/context/%s/%s/feed' % (context[:2], context) - self.touch( - (blob_path, '{}'), - (blob_path + '.blob', json.dumps({})), - ) - pipe = checkin('/', context) log_path = tests.tmpdir + '/.sugar/default/logs/%s.log' % context self.assertEqual([ @@ -57,27 +51,20 @@ class InjectorTest(tests.Test): 'notes': '', }) - blob_path = 'remote/context/%s/%s/feed' % (context[:2], context) - self.touch( - (blob_path, '{}'), - (blob_path + '.blob', json.dumps({ - 'versions': { - '1': { - '*-*': { - 'commands': { - 'activity': { - 'exec': 'echo', - }, - }, - 'stability': 'stable', - 'guid': impl, - 'size': 0, - }, + remote.put(['context', context, 'versions'], { + '1': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'echo', }, }, - })), - ) - os.unlink('cache/context/%s/%s/feed.meta' % (context[:2], context)) + 'stability': 'stable', + 'guid': impl, + 'size': 0, + }, + }, + }) pipe = checkin('/', context) log_path = tests.tmpdir + '/.sugar/default/logs/%s_1.log' % context @@ -127,28 +114,21 @@ class InjectorTest(tests.Test): 'stability': 'stable', 'notes': '', }) - - blob_path = 'remote/context/%s/%s/feed' % (context[:2], context) - self.touch( - (blob_path, '{}'), - (blob_path + '.blob', json.dumps({ - 'versions': { - '1': { - '*-*': { - 'commands': { - 'activity': { - 'exec': 'false', - }, - }, - 'stability': 'stable', - 'guid': impl, - 'size': 0, - 'extract': 'TestActivitry', - }, + remote.put(['context', context, 'versions'], { + '1': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'false', }, }, - })), - ) + 'stability': 'stable', + 'guid': impl, + 'size': 0, + 'extract': 'TestActivitry', + }, + }, + }) blob_path = 'remote/implementation/%s/%s/data' % (impl[:2], impl) self.touch((blob_path, '{}')) @@ -186,41 +166,34 @@ class InjectorTest(tests.Test): 'notes': '', }) - os.unlink('cache/context/%s/%s/feed.meta' % (context[:2], context)) - blob_path = 'remote/context/%s/%s/feed' % (context[:2], context) - self.touch( - (blob_path, '{}'), - (blob_path + '.blob', json.dumps({ - 'versions': { - '1': { - '*-*': { - 'commands': { - 'activity': { - 'exec': 'false', - }, - }, - 'stability': 'stable', - 'guid': impl, - 'size': 0, - 'extract': 'TestActivitry', - }, + remote.put(['context', context, 'versions'], { + '1': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'false', }, - '2': { - '*-*': { - 'commands': { - 'activity': { - 'exec': 'true', - }, - }, - 'stability': 'stable', - 'guid': impl_2, - 'size': 0, - 'extract': 'TestActivitry', - }, + }, + 'stability': 'stable', + 'guid': impl, + 'size': 0, + 'extract': 'TestActivitry', + }, + }, + '2': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'true', }, }, - })), - ) + 'stability': 'stable', + 'guid': impl_2, + 'size': 0, + 'extract': 'TestActivitry', + }, + }, + }) blob_path = 'remote/implementation/%s/%s/data' % (impl_2[:2], impl_2) self.touch((blob_path, '{}')) @@ -314,18 +287,12 @@ class InjectorTest(tests.Test): 'summary': 'summary', 'description': 'description', 'implement': 'dep1', + 'packages': { + lsb_release.distributor_id(): { + 'binary': ['dep1.bin'], + }, + }, }) - blob_path = 'remote/context/de/dep1/feed' - self.touch( - (blob_path, '{}'), - (blob_path + '.blob', json.dumps({ - 'packages': { - lsb_release.distributor_id(): { - 'binary': ['dep1.bin'], - }, - }, - })), - ) remote.post(['context'], { 'type': 'package', @@ -333,18 +300,12 @@ class InjectorTest(tests.Test): 'summary': 'summary', 'description': 'description', 'implement': 'dep2', + 'packages': { + lsb_release.distributor_id(): { + 'binary': ['dep2.bin'], + }, + }, }) - blob_path = 'remote/context/de/dep2/feed' - self.touch( - (blob_path, '{}'), - (blob_path + '.blob', json.dumps({ - 'packages': { - lsb_release.distributor_id(): { - 'binary': ['dep2.bin'], - }, - }, - })), - ) def resolve(names): with file('resolve', 'w') as f: diff --git a/tests/units/node.py b/tests/units/node.py index b4ec682..52f4b30 100755 --- a/tests/units/node.py +++ b/tests/units/node.py @@ -11,7 +11,7 @@ from sugar_network import node from sugar_network.toolkit.router import Unauthorized from sugar_network.node import stats from sugar_network.node.commands import NodeCommands -from sugar_network.resources.volume import Volume +from sugar_network.resources.volume import Volume, Request from sugar_network.resources.user import User @@ -371,7 +371,7 @@ class NodeTest(tests.Test): def call(cp, principal=None, content=None, **kwargs): - request = ad.Request(**kwargs) + request = Request(**kwargs) request.principal = principal request.content = content request.environ = {'HTTP_HOST': 'localhost'} diff --git a/tests/units/node_mount.py b/tests/units/node_mount.py index 763a73d..3f0f5e2 100755 --- a/tests/units/node_mount.py +++ b/tests/units/node_mount.py @@ -185,31 +185,21 @@ class NodeMountTest(tests.Test): 'stability': 'stable', 'notes': '', }) - - with file('mnt/context/%s/%s/feed' % (context[:2], context), 'w') as f: - json.dump({ - 'seqno': 0, - 'mime_type': 'application/octet-stream', - 'digest': 'digest', - }, f) - with file('mnt/context/%s/%s/feed.blob' % (context[:2], context), 'w') as f: - json.dump({ - 'versions': { - '1': { - '*-*': { - 'commands': { - 'activity': { - 'exec': 'echo', - }, - }, - 'stability': 'stable', - 'guid': impl, - 'size': 0, - 'extract': 'TestActivitry', + remote.put(['context', context, 'versions'], { + '1': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'echo', }, }, + 'stability': 'stable', + 'guid': impl, + 'size': 0, + 'extract': 'TestActivitry', }, - }, f) + }, + }) bundle = zipfile.ZipFile('bundle', 'w') bundle.writestr('TestActivitry/activity/activity.info', '\n'.join([ '[Activity]', diff --git a/tests/units/obs.py b/tests/units/obs.py new file mode 100755 index 0000000..a998d4a --- /dev/null +++ b/tests/units/obs.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# sugar-lint: disable + +from cStringIO import StringIO + +from __init__ import tests + +from sugar_network.toolkit import http +from sugar_network.node import obs + + +class ObsTest(tests.Test): + + def test_get_repos(self): + self.override(http, 'Client', Client(self, [ + (('GET', ['build', 'base']), {'allowed': (400, 404)}, [ + '<directory>', + ' <entry name="Debian-6.0" />', + ' <entry name="Fedora-11" />', + '</directory>', + ]), + (('GET', ['build', 'base', 'Debian-6.0']), {'allowed': (400, 404)}, [ + '<directory>', + ' <entry name="i586" />', + ' <entry name="x86_64" />', + '</directory>', + ]), + (('GET', ['build', 'base', 'Fedora-11']), {'allowed': (400, 404)}, [ + '<directory>', + ' <entry name="i586" />', + '</directory>', + ]), + ])) + + self.assertEqual([ + {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arches': ['i586', 'x86_64']}, + {'distributor_id': 'Fedora', 'name': 'Fedora-11', 'arches': ['i586']}, + ], + obs.get_repos()) + + def test_get_presolve_repos(self): + self.override(http, 'Client', Client(self, [ + (('GET', ['build', 'resolve']), {'allowed': (400, 404)}, [ + '<directory>', + ' <entry name="Debian-6.0" />', + ' <entry name="Fedora-11" />', + '</directory>', + ]), + (('GET', ['build', 'resolve', 'Debian-6.0']), {'allowed': (400, 404)}, [ + '<directory>', + ' <entry name="i586" />', + ' <entry name="x86_64" />', + '</directory>', + ]), + (('GET', ['build', 'resolve', 'Fedora-11']), {'allowed': (400, 404)}, [ + '<directory>', + ' <entry name="i586" />', + '</directory>', + ]), + ])) + + self.assertEqual([ + {'name': 'Debian-6.0', 'arch': 'i586'}, + {'name': 'Debian-6.0', 'arch': 'x86_64'}, + {'name': 'Fedora-11', 'arch': 'i586'}, + ], + obs.get_presolve_repos()) + + def test_resolve(self): + self.override(http, 'Client', Client(self, [ + (('GET', ['resolve']), + {'allowed': (400, 404), 'params': { + 'project': 'base', + 'repository': 'repo', + 'arch': 'arch', + 'package': 'pkg1', + }}, + [ '<resolve>', + ' <binary name="pygame" url="http://pkg1.prm" />', + '</resolve>', + ], + ), + (('GET', ['resolve']), + {'allowed': (400, 404), 'params': { + 'project': 'base', + 'repository': 'repo', + 'arch': 'arch', + 'package': 'pkg2', + }}, + [ '<resolve>', + ' <binary name="pygame" url="http://pkg2.prm" />', + '</resolve>', + ], + ), + ])) + + obs.resolve('repo', 'arch', ['pkg1', 'pkg2']) + + def test_presolve(self): + self.override(http, 'Client', Client(self, [ + (('GET', ['resolve']), + {'allowed': (400, 404), 'params': { + 'project': 'resolve', + 'repository': 'repo', + 'arch': 'arch', + 'package': 'pkg1', + 'withdeps': '1', + 'exclude': 'sugar', + }}, + [ '<resolve>', + ' <binary name="pkg1-1" url="http://pkg1-1.prm" />', + ' <binary name="pkg1-2" url="http://pkg1-2.prm" />', + '</resolve>', + ], + ), + (('GET', ['resolve']), + {'allowed': (400, 404), 'params': { + 'project': 'resolve', + 'repository': 'repo', + 'arch': 'arch', + 'package': 'pkg2', + 'withdeps': '1', + 'exclude': 'sugar', + }}, + [ '<resolve>', + ' <binary name="pkg2-1" url="http://pkg2-1.prm" />', + ' <binary name="pkg2-2" url="http://pkg2-2.prm" />', + '</resolve>', + ], + ), + ])) + + self.assertEqual([ + {'distributor_id': 'Fedora', 'url': 'http://pkg1-1.prm', 'name': 'pkg1-1'}, + {'distributor_id': 'Fedora', 'url': 'http://pkg1-2.prm', 'name': 'pkg1-2'}, + {'distributor_id': 'Fedora', 'url': 'http://pkg2-1.prm', 'name': 'pkg2-1'}, + {'distributor_id': 'Fedora', 'url': 'http://pkg2-2.prm', 'name': 'pkg2-2'}, + ], + obs.presolve('repo', 'arch', ['pkg1', 'pkg2'])) + + +class Response(object): + + headers = {'Content-Type': 'text/xml'} + raw = None + status_code = 200 + + +class Client(object): + + def __init__(self, test, calls): + self.test = test + self.calls = calls[:] + + def request(self, *args, **kwargs): + assert self.calls + args_, kwargs_, reply = self.calls.pop(0) + self.test.assertEqual((args_, kwargs_), (args, kwargs)) + response = Response() + response.raw = StringIO(''.join(reply)) + return response + + def __call__(self, url): + return self + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/remote_mount.py b/tests/units/remote_mount.py index ed8beef..49c08e1 100755 --- a/tests/units/remote_mount.py +++ b/tests/units/remote_mount.py @@ -159,7 +159,7 @@ class RemoteMountTest(tests.Test): ], events) - def test_Subscription_NotifyOnline(self): + def ___test_Subscription_NotifyOnline(self): self.start_ipc_and_restful_server() remote = IPCClient(mountpoint='/') local = IPCClient(mountpoint='~') |