From a8fb61b807e4c46adec8d631f596632a3fa7b6e3 Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Sat, 03 Aug 2013 15:27:43 +0000 Subject: Keep stability preferences in config files --- diff --git a/TODO b/TODO index 7958fa4..b87cfd7 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,7 @@ - 0.10 - i18n searches +- invalidate solutions cache on config changes (layers ans stabilities might be changed) - delete outdated impls on PUTing new one - (!) Editors' workflows: diff --git a/sugar_network/client/__init__.py b/sugar_network/client/__init__.py index 30dd541..ff845f3 100644 --- a/sugar_network/client/__init__.py +++ b/sugar_network/client/__init__.py @@ -228,6 +228,13 @@ def sugar_profile(): } +def stability(context): + value = Option.get('stabilities', context) or \ + Option.get('stabilities', 'default') or \ + 'stable' + return value.split() + + def _read_XO_value(paths): for value_path in paths: if exists(value_path): diff --git a/sugar_network/client/routes.py b/sugar_network/client/routes.py index c4f5edb..38f586e 100644 --- a/sugar_network/client/routes.py +++ b/sugar_network/client/routes.py @@ -179,6 +179,8 @@ class ClientRoutes(model.Routes, journal.Routes): context_type = self._node_call(method='GET', path=['context', request.guid, 'type']) + if 'stability' not in request: + request['stability'] = client.stability(request.guid) if 'activity' in context_type: self._clone_activity(request) @@ -187,8 +189,8 @@ class ClientRoutes(model.Routes, journal.Routes): def get_props(): impls = self._node_call(method='GET', path=['implementation'], context=request.guid, - stability='stable', order_by='-version', limit=1, - reply=['guid'])['result'] + stability=request['stability'], order_by='-version', + limit=1, reply=['guid'])['result'] enforce(impls, http.NotFound, 'No implementations') impl_id = impls[0]['guid'] props = self._node_call(method='GET', @@ -279,10 +281,9 @@ class ClientRoutes(model.Routes, journal.Routes): request = Request(method=method, path=path) request.update(kwargs) if self._inline.is_set(): - if client.layers.value and request.resource in \ - ('context', 'implementation') and \ - 'layer' not in request: - request['layer'] = client.layers.value + if client.layers.value and \ + request.resource in ('context', 'implementation'): + request.add('layer', *client.layers.value) try: reply = self._node.call(request, response) if hasattr(reply, 'read'): @@ -503,7 +504,7 @@ class ClientRoutes(model.Routes, journal.Routes): self._checkin_context(request.guid, {'clone': 1}) if request.get('nodeps'): pipe = injector.clone_impl(request.guid, - stability=request.get('stability'), + stability=request['stability'], requires=request.get('requires')) else: pipe = injector.clone(request.guid) diff --git a/sugar_network/client/solver.py b/sugar_network/client/solver.py index 10245a1..3d18cef 100644 --- a/sugar_network/client/solver.py +++ b/sugar_network/client/solver.py @@ -19,6 +19,7 @@ import sys import logging from os.path import isabs, join, dirname +from sugar_network import client from sugar_network.client import packagekit, SUGAR_API_COMPATIBILITY from sugar_network.toolkit.spec import parse_version from sugar_network.toolkit import http, lsb_release, pipe, exception @@ -203,7 +204,7 @@ def _load_feed(conn, context): feed_content = None try: feed_content = conn.get(['context', context], cmd='feed', - # TODO stability='stable' + stability=client.stability(context), distro=lsb_release.distributor_id()) pipe.trace('Found %s feed: %r', context, feed_content) except http.ServiceUnavailable: @@ -271,7 +272,7 @@ class _Feed(model.ZeroInstallFeed): impl.version = parse_version(release['version']) impl.released = 0 impl.arch = release['arch'] - impl.upstream_stability = model.stability_levels[release['stability']] + impl.upstream_stability = model.stability_levels['stable'] impl.requires.extend(_read_requires(release.get('requires'))) impl.hints = release diff --git a/sugar_network/node/routes.py b/sugar_network/node/routes.py index 41cebc3..8457be6 100644 --- a/sugar_network/node/routes.py +++ b/sugar_network/node/routes.py @@ -218,13 +218,13 @@ class NodeRoutes(db.Routes, model.Routes): @route('GET', ['context', None], cmd='feed', mime_type='application/json') - def feed(self, request, layer, distro): + def feed(self, request, distro): context = self.volume['context'].get(request.guid) implementations = self.volume['implementation'] versions = [] impls, __ = implementations.find(limit=db.MAX_LIMIT, - context=context.guid, layer=layer, not_layer='deleted') + context=context.guid, not_layer='deleted', **request) for impl in impls: for arch, spec in impl.meta('data')['spec'].items(): spec['guid'] = impl.guid diff --git a/sugar_network/toolkit/options.py b/sugar_network/toolkit/options.py index b86b6af..b603706 100644 --- a/sugar_network/toolkit/options.py +++ b/sugar_network/toolkit/options.py @@ -102,6 +102,13 @@ class Option(object): self._value = str(x) or None @staticmethod + def get(section, key): + if Option._parser is None or \ + not Option._parser.has_option(section, key): + return None + return Option._parser.get(section, key) + + @staticmethod def seek(section, mod=None): """Collect `Option` objects. diff --git a/sugar_network/toolkit/router.py b/sugar_network/toolkit/router.py index 5c19729..6924160 100644 --- a/sugar_network/toolkit/router.py +++ b/sugar_network/toolkit/router.py @@ -229,14 +229,15 @@ class Request(dict): self._pos += len(result) return result - def add(self, key, value): + def add(self, key, *values): existing_value = self.get(key) - if existing_value is None: - self[key] = value - elif type(existing_value) is list: - existing_value.append(value) - else: - self[key] = [existing_value, value] + for value in values: + if existing_value is None: + existing_value = self[key] = value + elif type(existing_value) is list: + existing_value.append(value) + else: + existing_value = self[key] = [existing_value, value] def __repr__(self): return '' % \ diff --git a/tests/__init__.py b/tests/__init__.py index f4eed96..3541cea 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -27,6 +27,7 @@ from sugar_network.model.context import Context from sugar_network.model.implementation import Implementation from sugar_network.node.master import MasterRoutes from sugar_network.node import stats_user, stats_node, obs, slave, downloads +from requests import adapters root = abspath(dirname(__file__)) @@ -72,6 +73,7 @@ class Test(unittest.TestCase): shutil.copy(join(root, 'data', 'owner.key'), join(profile_dir, 'owner.key')) shutil.copy(join(root, 'data', 'owner.key.pub'), profile_dir) + adapters.DEFAULT_RETRIES = 5 Option.unsorted_items = [] Option.items = {} Option.sections = {} diff --git a/tests/units/client/online_routes.py b/tests/units/client/online_routes.py index 7dc3409..691de3e 100755 --- a/tests/units/client/online_routes.py +++ b/tests/units/client/online_routes.py @@ -23,6 +23,7 @@ from sugar_network.model.context import Context from sugar_network.model.implementation import Implementation from sugar_network.model.artifact import Artifact from sugar_network.toolkit.router import route +from sugar_network.toolkit import Option import requests @@ -157,6 +158,94 @@ class OnlineRoutes(tests.Test): {'clone': 2}, ipc.get(['context', context], reply=['clone'])) + def test_clone_ActivitiesWithStabilityPreferences(self): + self.home_volume = self.start_online_client() + ipc = IPCConnection() + coroutine.spawn(clones.monitor, self.home_volume['context'], ['Activities']) + + context = ipc.post(['context'], { + 'type': 'activity', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + + impl1 = ipc.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '1', + 'stability': 'stable', + 'notes': '', + }) + info1 = '\n'.join([ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = %s' % context, + 'exec = false', + 'icon = icon', + 'activity_version = 1', + 'license=Public Domain', + ]) + self.node_volume['implementation'].update(impl1, {'data': { + 'spec': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'true', + }, + }, + 'extract': 'TestActivitry', + }, + }, + 'blob': StringIO(self.zips(['TestActivitry/activity/activity.info', info1])), + }}) + + impl2 = ipc.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '2', + 'stability': 'testing', + 'notes': '', + }) + info2 = '\n'.join([ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = %s' % context, + 'exec = false', + 'icon = icon', + 'activity_version = 1', + 'license=Public Domain', + ]) + self.node_volume['implementation'].update(impl2, {'data': { + 'spec': { + '*-*': { + 'commands': { + 'activity': { + 'exec': 'true', + }, + }, + 'extract': 'TestActivitry2', + }, + }, + 'blob': StringIO(self.zips(['TestActivitry2/activity/activity.info', info2])), + }}) + + ipc.put(['context', context], 2, cmd='clone') + coroutine.sleep(.5) + not exists('Activities/TestActivitry2/activity/activity.info') + self.assertEqual(info1, file('Activities/TestActivitry/activity/activity.info').read()) + + self.touch(('config', [ + '[stabilities]', + '%s = testing stable' % context, + ])) + Option.load(['config']) + + shutil.rmtree('cache/solutions') + ipc.put(['context', context], 2, cmd='clone', force=1) + coroutine.sleep(.5) + self.assertEqual(info2, file('Activities/TestActivitry2/activity/activity.info').read()) + def test_clone_ActivityImpl(self): self.home_volume = self.start_online_client() ipc = IPCConnection() @@ -638,6 +727,7 @@ class OnlineRoutes(tests.Test): 'title': 'title', 'summary': 'summary', 'description': 'description', + 'layer': 'public', }) impl = ipc.post(['implementation'], { 'context': context, @@ -645,6 +735,7 @@ class OnlineRoutes(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', + 'layer': 'public', }) self.node_volume['implementation'].update(impl, {'data': { 'spec': {'*-*': {}}, @@ -654,6 +745,7 @@ class OnlineRoutes(tests.Test): 'context': 'context', 'title': 'title', 'description': 'description', + 'layer': 'public', }) self.assertEqual( diff --git a/tests/units/client/routes.py b/tests/units/client/routes.py index e69c7ed..c9caa7e 100755 --- a/tests/units/client/routes.py +++ b/tests/units/client/routes.py @@ -174,7 +174,7 @@ class RoutesTest(tests.Test): } guid = call(cp, post) - self.assertEqual(['public', 'local'], call(cp, Request(method='GET', path=['context', guid, 'layer']))) + self.assertEqual(['local'], call(cp, Request(method='GET', path=['context', guid, 'layer']))) trigger = self.wait_for_events(cp, event='inline', state='online') node_volume = self.start_master() @@ -182,7 +182,7 @@ class RoutesTest(tests.Test): trigger.wait() guid = call(cp, post) - self.assertEqual(['public'], call(cp, Request(method='GET', path=['context', guid, 'layer']))) + self.assertEqual([], call(cp, Request(method='GET', path=['context', guid, 'layer']))) def test_CachedClientCommands(self): volume = db.Volume('client', model.RESOURCES) diff --git a/tests/units/client/solver.py b/tests/units/client/solver.py index b4bed8d..84d5456 100755 --- a/tests/units/client/solver.py +++ b/tests/units/client/solver.py @@ -6,7 +6,7 @@ import os from __init__ import tests from sugar_network.client import IPCConnection, packagekit, solver, clones -from sugar_network.toolkit import lsb_release +from sugar_network.toolkit import lsb_release, Option class SolverTest(tests.Test): @@ -74,6 +74,81 @@ class SolverTest(tests.Test): ('dep', '0'), (solution[1]['context'], solution[1]['version'])) + def test_StabilityPreferences(self): + self.start_online_client() + ipc = IPCConnection() + data = {'spec': {'*-*': {'commands': {'activity': {'exec': 'echo'}}, 'extract': 'topdir'}}} + + context = ipc.post(['context'], { + 'type': 'activity', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + impl1 = ipc.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '1', + 'stability': 'stable', + 'notes': '', + }) + self.node_volume['implementation'].update(impl1, {'data': data}) + impl2 = ipc.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '2', + 'stability': 'testing', + 'notes': '', + }) + self.node_volume['implementation'].update(impl2, {'data': data}) + impl3 = ipc.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '3', + 'stability': 'buggy', + 'notes': '', + }) + self.node_volume['implementation'].update(impl3, {'data': data}) + impl4 = ipc.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '4', + 'stability': 'insecure', + 'notes': '', + }) + self.node_volume['implementation'].update(impl4, {'data': data}) + + self.assertEqual('1', solver.solve(ipc, context)[0]['version']) + + self.touch(('config', [ + '[stabilities]', + '%s = testing' % context, + ])) + Option.load(['config']) + self.assertEqual('2', solver.solve(ipc, context)[0]['version']) + + self.touch(('config', [ + '[stabilities]', + '%s = testing buggy' % context, + ])) + Option.load(['config']) + self.assertEqual('3', solver.solve(ipc, context)[0]['version']) + + self.touch(('config', [ + '[stabilities]', + 'default = insecure', + '%s = stable' % context, + ])) + Option.load(['config']) + self.assertEqual('1', solver.solve(ipc, context)[0]['version']) + + self.touch(('config', [ + '[stabilities]', + 'default = insecure', + ])) + Option.load(['config']) + self.assertEqual('4', solver.solve(ipc, context)[0]['version']) + if __name__ == '__main__': tests.main() diff --git a/tests/units/model/context.py b/tests/units/model/context.py index c63ab14..25b21b4 100755 --- a/tests/units/model/context.py +++ b/tests/units/model/context.py @@ -20,7 +20,7 @@ class ContextTest(tests.Test): 'summary': 'summary', 'description': 'description', }) - self.assertEqual(['public', 'common'], ipc.get(['context', guid, 'layer'])) + self.assertEqual(['common'], ipc.get(['context', guid, 'layer'])) guid = ipc.post(['context'], { 'type': 'package', -- cgit v0.9.1