Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2013-08-03 15:27:43 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2013-08-03 15:27:43 (GMT)
commita8fb61b807e4c46adec8d631f596632a3fa7b6e3 (patch)
treeb8a7cdec8943a291921d2b5ffa134c8ed8a57ea2
parent18b264b102f50be009125b54b8b2c9793d505a8d (diff)
Keep stability preferences in config files
-rw-r--r--TODO1
-rw-r--r--sugar_network/client/__init__.py7
-rw-r--r--sugar_network/client/routes.py15
-rw-r--r--sugar_network/client/solver.py5
-rw-r--r--sugar_network/node/routes.py4
-rw-r--r--sugar_network/toolkit/options.py7
-rw-r--r--sugar_network/toolkit/router.py15
-rw-r--r--tests/__init__.py2
-rwxr-xr-xtests/units/client/online_routes.py92
-rwxr-xr-xtests/units/client/routes.py4
-rwxr-xr-xtests/units/client/solver.py77
-rwxr-xr-xtests/units/model/context.py2
12 files changed, 209 insertions, 22 deletions
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 '<Request method=%s path=%r cmd=%s query=%r>' % \
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',