Merge lp:~mvo/pkgme-devportal/add-configuration-for-library-overrides into lp:pkgme-devportal

Proposed by Michael Vogt
Status: Merged
Approved by: Michael Vogt
Approved revision: 59
Merged at revision: 54
Proposed branch: lp:~mvo/pkgme-devportal/add-configuration-for-library-overrides
Merge into: lp:pkgme-devportal
Diff against target: 366 lines (+153/-64)
6 files modified
devportalbinary/binary.py (+10/-2)
devportalbinary/configuration.py (+69/-0)
devportalbinary/database.py (+1/-53)
devportalbinary/testing.py (+35/-1)
devportalbinary/tests/test_binary.py (+32/-1)
devportalbinary/tests/test_database.py (+6/-7)
To merge this branch: bzr merge lp:~mvo/pkgme-devportal/add-configuration-for-library-overrides
Reviewer Review Type Date Requested Status
James Westby Approve
Michael Vogt (community) Needs Resubmitting
Jonathan Lange Approve
Review via email: mp+115125@code.launchpad.net

Commit message

Allow configuring overrides for the lib->package mapping.

Description of the change

This branch provides a way to override library dependencies. The background for this is bug #1025186. Often a library is provided by multiple alternative versions that can not be co-installed. Common cases are libgl1-mesa-{glx,swx11}. To catch them a simple override mechanism is provided.

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

 * Really like splitting 'configuration' into a different module. Thanks.
 * ``write_config`` should probably be a Fixture that just sets the config. Not necessary to change in this branch, but if you're feeling
 * I would have thought that the config should be '<library>: <recommended package>'. I don't know if I'm right or you're right or even if the patch should be changed at this point in the absence of data. Probably OK to merge.
 * There really ought to be an example configuration that shows this
 * We're probably going to want a web control for this. Pinging LOSAs every time we want to add something is going to suck.

review: Approve
Revision history for this message
James Westby (james-w) wrote :

Hi,

I had the same instinct as jml with the '<library>: <recommended package>' config (making it a set of recommended packages to match the API though). It seems overly broad to do the search, and the dict would likely be quicker
if there were a lot of overrides.

I'm also unsure that a config is the right thing for this, becuase aren't the overrides going
to be the same for everyone everywhere? I guess not, and it's hard to know what will happen, so
let's go with this.

I'm for this landing quickly, but I think because changing the config would be tricky because
of backwards compatibility we should change it to do the dict method rather than the search
method before landing. What do you think?

Thanks,

James

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks to both of you, I like the idea(s) and will change the config format. I'm unsure about the config too TBH, but it seems like guessing on some heuristics is not better so until we find a better solution having a override is probably ok(ish).

I will not address the "add-webinterface" for now as this should be a seperate branch :)

Revision history for this message
Michael Vogt (mvo) wrote :

I addressed the above points one (except for the webinterface). I hope this is in line with what you have in mind, I'm happy to refactor further if needed :)

review: Needs Resubmitting
Revision history for this message
James Westby (james-w) :
review: Approve
Revision history for this message
Canonical CA Tarmac (ca-tarmac) wrote :
Download full text (5.1 KiB)

The attempt to merge lp:~mvo/pkgme-devportal/add-configuration-for-library-overrides into lp:pkgme-devportal failed. Below is the output from the failed tests.

Downloading/unpacking mock (from -r test-dependencies.txt (line 1))
  Running setup.py egg_info for package mock
    warning: no files found matching '*.png' under directory 'docs'
    warning: no files found matching '*.css' under directory 'docs'
    warning: no files found matching '*.html' under directory 'docs'
    warning: no files found matching '*.js' under directory 'docs'
Installing collected packages: mock
  Running setup.py install for mock
    warning: no files found matching '*.png' under directory 'docs'
    warning: no files found matching '*.css' under directory 'docs'
    warning: no files found matching '*.html' under directory 'docs'
    warning: no files found matching '*.js' under directory 'docs'
Successfully installed mock
Cleaning up...
Tests running...
======================================================================
ERROR: tests.test_binary.BinaryBackendTests.test_config_glue_lib_overrides
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/tarmac-tmp/tmp4oMKa_/devportalbinary/tests/test_binary.py", line 305, in test_config_glue_lib_overrides
    self.useFixture(ConfigFileFixture())
  File "/mnt/tarmac-tmp/tmp4oMKa_/virtualenv/lib/python2.6/site-packages/testtools-0.9.15-py2.6.egg/testtools/testcase.py", line 579, in useFixture
    fixture.setUp()
  File "devportalbinary/testing.py", line 126, in setUp
    with open(CONF_FILE, 'w') as f:
IOError: [Errno 2] No such file or directory: '~/.config/pkgme-binary/conf'
======================================================================
ERROR: discover.ModuleImportFailure.testing
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/tarmac-tmp/tmp4oMKa_/virtualenv/lib/python2.6/site-packages/discover.py", line 64, in testFailure
    raise exception
ImportError: Failed to import test module: testing
Traceback (most recent call last):
  File "/mnt/tarmac-tmp/tmp4oMKa_/virtualenv/lib/python2.6/site-packages/discover.py", line 288, in _find_tests
    module = self._get_module_from_name(name)
  File "/mnt/tarmac-tmp/tmp4oMKa_/virtualenv/lib/python2.6/site-packages/discover.py", line 266, in _get_module_from_name
    __import__(name)
  File "/mnt/tarmac-tmp/tmp4oMKa_/devportalbinary/testing.py", line 30, in <module>
    from .configuration import CONF_FILE
ValueError: Attempted relative import in non-package

Ran 115 tests in 0.384s
FAILED (failures=2)

Branched 118 revision(s).
/mnt/tarmac-tmp/tmp4oMKa_/virtualenv/lib/python2.6/site-packages/distribute-0.6.10-py2.6.egg/setuptools/command/bdist_egg.py:422: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  symbols = dict.fromkeys(iter_symbols(code))
simplejson/_speedups.c: In function 'encoder_listencode_obj':
simplejson/_speedups.c:2269: warning: comparison of distinct pointer types lacks a cast
simplejson/_speedups.c:2269: warning: passing argument 2 of 'PyTyp...

Read more...

58. By Michael Vogt

devportalbinary/testing.py: do not use relative import (tarmac is on py2.6 apparently)

59. By Michael Vogt

devportalbinary/testing.py: add os.path.expanduser() to fix tarmac failure

Revision history for this message
Michael Vogt (mvo) wrote :

Setting back to approved to let tarmac try again.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'devportalbinary/binary.py'
--- devportalbinary/binary.py 2012-03-28 16:34:58 +0000
+++ devportalbinary/binary.py 2012-07-16 18:51:18 +0000
@@ -40,11 +40,11 @@
40from pkgme.errors import PkgmeError40from pkgme.errors import PkgmeError
41from pkgme.run_script import run_subprocess41from pkgme.run_script import run_subprocess
4242
43from devportalbinary.configuration import load_configuration
43from devportalbinary.database import PackageDatabase44from devportalbinary.database import PackageDatabase
44from devportalbinary.metadata import MetadataBackend45from devportalbinary.metadata import MetadataBackend
4546
4647
47
48# XXX: No idea about how icons will be there. Ignoring for now.48# XXX: No idea about how icons will be there. Ignoring for now.
4949
5050
@@ -160,10 +160,18 @@
160160
161161
162def get_packages_for_libraries(library_names, arch):162def get_packages_for_libraries(library_names, arch):
163 conf = load_configuration()
164 lib_overrides = conf.options.lib_overrides_overrides
163 db = PackageDatabase.create()165 db = PackageDatabase.create()
164 deps = set()166 deps = set()
165 for lib in library_names:167 for lib in library_names:
166 new_deps = db.get_dependencies(lib)168 # ensure that overrides always "trump" the existing set if they
169 # are found
170 if lib in lib_overrides:
171 new_deps = set([lib_overrides[lib]])
172 else:
173 new_deps = db.get_dependencies(lib)
174
167 if not new_deps:175 if not new_deps:
168 raise UnknownDependency('Can\'t find dependency for "%s".' % lib)176 raise UnknownDependency('Can\'t find dependency for "%s".' % lib)
169 deps |= new_deps177 deps |= new_deps
170178
=== added file 'devportalbinary/configuration.py'
--- devportalbinary/configuration.py 1970-01-01 00:00:00 +0000
+++ devportalbinary/configuration.py 2012-07-16 18:51:18 +0000
@@ -0,0 +1,69 @@
1import os
2
3try:
4 from configglue import glue
5except ImportError:
6 from configglue.pyschema import glue
7
8try:
9 from configglue.schema import (
10 Schema,
11 Section,
12 DictOption,
13 StringOption,
14 )
15except ImportError:
16 from configglue.pyschema.schema import (
17 Schema,
18 ConfigSection as Section,
19 DictConfigOption as DictOption,
20 StringConfigOption as StringOption,
21 )
22
23
24# XXX: 'pkgme-binary' is the historic name of this package. Change this
25# to look first in ~/.config/pkgme-devportal/conf and then fall back to
26# this one. Once production systems are updated to the new config, remove
27# the fallback.
28CONF_FILE = '~/.config/pkgme-binary/conf'
29
30
31class DevportalSchema(Schema):
32
33 # database
34 database = Section()
35 database.db_type = StringOption(default='sqlite',
36 help=('The database to use, one of "sqlite", or "postgres"'))
37 database.host = StringOption(
38 help='The database host (for postgres)')
39 database.port = StringOption(
40 help='The database port (for postgres)')
41 database.username = StringOption(
42 help='The database username (for postgres)')
43 database.password = StringOption(
44 help='The database password (for postgres)')
45 database.db_name = StringOption(
46 help='The database name (for postgres)')
47 database.path = StringOption(
48 help='The path to the database file (for sqlite)')
49
50 scan_mode = StringOption(
51 help='To scan binary or source packages.')
52
53 # overrides
54 lib_overrides = Section()
55 overrides = { 'libasound.so.2': 'libasound2',
56 'libgl.so.1': 'libgl1-mesa-glx',
57 }
58 lib_overrides.overrides = DictOption(
59 default=overrides,
60 help='mapping of library name to pkgname to force picking selected '
61 'dependencies')
62
63
64def load_configuration():
65 config_location = os.path.expanduser(CONF_FILE)
66 config_files = []
67 if os.path.exists(config_location):
68 config_files.append(config_location)
69 return glue.configglue(DevportalSchema, config_files)
070
=== modified file 'devportalbinary/database.py'
--- devportalbinary/database.py 2012-04-26 12:10:00 +0000
+++ devportalbinary/database.py 2012-07-16 18:51:18 +0000
@@ -8,23 +8,6 @@
8import tempfile8import tempfile
99
10from bzrlib import urlutils10from bzrlib import urlutils
11try:
12 from configglue import glue
13except ImportError:
14 from configglue.pyschema import glue
15
16try:
17 from configglue.schema import (
18 Schema,
19 Section,
20 StringOption,
21 )
22except ImportError:
23 from configglue.pyschema.schema import (
24 Schema,
25 ConfigSection as Section,
26 StringConfigOption as StringOption,
27 )
28from fixtures import (11from fixtures import (
29 Fixture,12 Fixture,
30 TempDir,13 TempDir,
@@ -37,6 +20,7 @@
37from pkgme.run_script import run_subprocess20from pkgme.run_script import run_subprocess
38from storm.locals import create_database, Store21from storm.locals import create_database, Store
3922
23from .configuration import load_configuration
40from .utils import download_file24from .utils import download_file
4125
4226
@@ -274,47 +258,11 @@
274 yield library, dependency258 yield library, dependency
275259
276260
277class PackageDBSchema(Schema):
278
279 database = Section()
280 database.db_type = StringOption(default='sqlite',
281 help=('The database to use, one of "sqlite", or "postgres"'))
282 database.host = StringOption(
283 help='The database host (for postgres)')
284 database.port = StringOption(
285 help='The database port (for postgres)')
286 database.username = StringOption(
287 help='The database username (for postgres)')
288 database.password = StringOption(
289 help='The database password (for postgres)')
290 database.db_name = StringOption(
291 help='The database name (for postgres)')
292 database.path = StringOption(
293 help='The path to the database file (for sqlite)')
294
295 scan_mode = StringOption(
296 help='To scan binary or source packages.')
297
298
299def load_configuration():
300 config_location = os.path.expanduser(PackageDatabase.CONF_FILE)
301 config_files = []
302 if os.path.exists(config_location):
303 config_files.append(config_location)
304 return glue.configglue(PackageDBSchema, config_files)
305
306
307class PackageDatabase(object):261class PackageDatabase(object):
308262
309 SQLITE = 'sqlite'263 SQLITE = 'sqlite'
310 POSTGRES = 'postgres'264 POSTGRES = 'postgres'
311265
312 # XXX: 'pkgme-binary' is the historic name of this package. Change this
313 # to look first in ~/.config/pkgme-devportal/conf and then fall back to
314 # this one. Once production systems are updated to the new config, remove
315 # the fallback.
316 CONF_FILE = '~/.config/pkgme-binary/conf'
317
318 def __init__(self, store):266 def __init__(self, store):
319 self._store = store267 self._store = store
320268
321269
=== modified file 'devportalbinary/testing.py'
--- devportalbinary/testing.py 2012-07-05 07:11:28 +0000
+++ devportalbinary/testing.py 2012-07-16 18:51:18 +0000
@@ -27,6 +27,8 @@
27from devportalbinary.binary import MetadataBackend27from devportalbinary.binary import MetadataBackend
28from devportalbinary.database import PackageDatabase28from devportalbinary.database import PackageDatabase
2929
30from devportalbinary.configuration import CONF_FILE
31
30class IsChildPath(Matcher):32class IsChildPath(Matcher):
3133
32 def __init__(self, parent_path):34 def __init__(self, parent_path):
@@ -86,7 +88,7 @@
86 self.useFixture(TempHomeDirFixture())88 self.useFixture(TempHomeDirFixture())
87 tempdir = self.useFixture(TempDir())89 tempdir = self.useFixture(TempDir())
88 db_path = os.path.join(tempdir.path, 'test-db')90 db_path = os.path.join(tempdir.path, 'test-db')
89 config_file_path = os.path.expanduser(PackageDatabase.CONF_FILE)91 config_file_path = os.path.expanduser(CONF_FILE)
90 os.makedirs(os.path.dirname(config_file_path))92 os.makedirs(os.path.dirname(config_file_path))
91 with open(config_file_path, 'w') as f:93 with open(config_file_path, 'w') as f:
92 f.write('[database]\ndb_type=sqlite\npath=%s\n' % db_path)94 f.write('[database]\ndb_type=sqlite\npath=%s\n' % db_path)
@@ -107,6 +109,28 @@
107 self.useFixture(EnvironmentVariableFixture("HOME", self.path))109 self.useFixture(EnvironmentVariableFixture("HOME", self.path))
108110
109111
112class ConfigFileFixture(TempHomeDirFixture):
113 """Create a lib_overrides config file.
114
115 This also provides a example for the [lib_overrides] section syntax
116 """
117
118 # for the test, needs to be in sync with the config file below
119 TEST_LIBS = { 'libasound.so.2' : 'libasound2',
120 'libgl.so.1' : 'libgl1-mesa-glx',
121 }
122
123 def setUp(self):
124 super(ConfigFileFixture, self).setUp()
125 os.makedirs(os.path.dirname(os.path.expanduser(CONF_FILE)))
126 with open(os.path.expanduser(CONF_FILE), 'w') as f:
127 f.write("""
128[lib_overrides]
129libasound.so.2 = libasound2
130libgl1.so.1 = libglx-mesa-dri
131""")
132
133
110class MetadataFixture(Fixture):134class MetadataFixture(Fixture):
111 """Create a metadata file to use.135 """Create a metadata file to use.
112136
@@ -188,3 +212,13 @@
188 if path is None:212 if path is None:
189 path = self.useFixture(TempdirFixture()).path213 path = self.useFixture(TempdirFixture()).path
190 return self.BACKEND(path)214 return self.BACKEND(path)
215
216
217def write_config(conf_file, conf_key, **kwargs):
218 config_path = os.path.expanduser(conf_file)
219 if not os.path.isdir(os.path.dirname(config_path)):
220 os.makedirs(os.path.dirname(config_path))
221 with open(config_path, 'w') as f:
222 f.write("[%s]\n" % conf_key)
223 for key, value in kwargs.items():
224 f.write("%s=%s\n" % (key, value))
191225
=== modified file 'devportalbinary/tests/test_binary.py'
--- devportalbinary/tests/test_binary.py 2012-03-16 14:08:44 +0000
+++ devportalbinary/tests/test_binary.py 2012-07-16 18:51:18 +0000
@@ -1,18 +1,22 @@
1# Copyright 2011-2012 Canonical Ltd. This software is licensed under the1# Copyright 2011-2012 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4import json
4import os5import os
5import shutil6import shutil
67
7from testtools import TestCase8from testtools import TestCase
8from testtools.matchers import StartsWith9from testtools.matchers import StartsWith
910
10from pkgme.testing import TempdirFixture11from pkgme.testing import (
12 TempdirFixture,
13 )
1114
12from devportalbinary.binary import (15from devportalbinary.binary import (
13 BinaryBackend,16 BinaryBackend,
14 get_file_type,17 get_file_type,
15 get_file_types,18 get_file_types,
19 get_packages_for_libraries,
16 get_shared_library_dependencies,20 get_shared_library_dependencies,
17 guess_dependencies,21 guess_dependencies,
18 guess_executable,22 guess_executable,
@@ -22,16 +26,23 @@
22 NoBinariesFound,26 NoBinariesFound,
23 UnknownDependency,27 UnknownDependency,
24 )28 )
29from devportalbinary.configuration import (
30 CONF_FILE,
31 load_configuration,
32 )
25from devportalbinary.metadata import (33from devportalbinary.metadata import (
26 MetadataBackend,34 MetadataBackend,
27 )35 )
28from devportalbinary.testing import (36from devportalbinary.testing import (
29 BackendTests,37 BackendTests,
30 BinaryFileFixture,38 BinaryFileFixture,
39 ConfigFileFixture,
31 DatabaseFixture,40 DatabaseFixture,
32 get_test_data_dir_path,41 get_test_data_dir_path,
33 get_test_data_file_path,42 get_test_data_file_path,
34 MetadataFixture,43 MetadataFixture,
44 TempHomeDirFixture,
45 write_config,
35 )46 )
3647
3748
@@ -288,3 +299,23 @@
288 self.assertEqual(299 self.assertEqual(
289 '/opt/%s/the-best' % (package_name,),300 '/opt/%s/the-best' % (package_name,),
290 backend.get_executable(package_name))301 backend.get_executable(package_name))
302
303 def test_config_glue_lib_overrides(self):
304 # XXX: this needs to be in sync with testing.py:ConfigFileFixture
305 self.useFixture(ConfigFileFixture())
306 conf = load_configuration()
307 self.assertEqual(
308 conf.options.lib_overrides_overrides, ConfigFileFixture.TEST_LIBS)
309
310 def test_get_lib_overrides_for_packages_for_libraries(self):
311 """Test that the configuration file overrides the found
312 library dependencies
313 """
314 db = self.useFixture(DatabaseFixture()).db
315 db.update_source_package('foo', [
316 [('libasound.so.2', 'libfoo')],
317 [('libasound.so.2', 'libbar')],
318 ])
319 self.assertEqual(
320 get_packages_for_libraries(["libasound.so.2"], "i386"),
321 set(["libasound2"]))
291322
=== modified file 'devportalbinary/tests/test_database.py'
--- devportalbinary/tests/test_database.py 2012-01-20 16:17:16 +0000
+++ devportalbinary/tests/test_database.py 2012-07-16 18:51:18 +0000
@@ -17,7 +17,11 @@
17 load_configuration,17 load_configuration,
18 PackageDatabase,18 PackageDatabase,
19 )19 )
20from devportalbinary.testing import TempHomeDirFixture20from devportalbinary.configuration import CONF_FILE
21from devportalbinary.testing import (
22 TempHomeDirFixture,
23 write_config,
24 )
2125
2226
23class ResultsIn(Matcher):27class ResultsIn(Matcher):
@@ -92,12 +96,7 @@
92 self.assertEqual(deps, set())96 self.assertEqual(deps, set())
9397
94 def write_database_config(self, **kwargs):98 def write_database_config(self, **kwargs):
95 config_path = os.path.expanduser(PackageDatabase.CONF_FILE)99 write_config(CONF_FILE, "database", **kwargs)
96 os.makedirs(os.path.dirname(config_path))
97 with open(config_path, 'w') as f:
98 f.write("[database]\n")
99 for key, value in kwargs.items():
100 f.write("%s=%s\n" % (key, value))
101100
102 def test_get_db_info_from_config_sqlite(self):101 def test_get_db_info_from_config_sqlite(self):
103 other_tempdir = self.useFixture(TempDir())102 other_tempdir = self.useFixture(TempDir())

Subscribers

People subscribed via source and target branches