Merge lp:~mvo/pkgme-devportal/more-architectures into lp:pkgme-devportal
- more-architectures
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | James Westby |
Approved revision: | 66 |
Merged at revision: | 88 |
Proposed branch: | lp:~mvo/pkgme-devportal/more-architectures |
Merge into: | lp:pkgme-devportal |
Prerequisite: | lp:~mvo/pkgme-devportal/raise-on-unsupported-architectures |
Diff against target: |
225 lines (+35/-30) 5 files modified
acceptance/tests/__init__.py (+3/-3) devportalbinary/binary.py (+3/-1) devportalbinary/database.py (+15/-12) devportalbinary/tests/test_binary.py (+4/-4) devportalbinary/tests/test_database.py (+10/-10) |
To merge this branch: | bzr merge lp:~mvo/pkgme-devportal/more-architectures |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby | Approve | ||
Review via email: mp+116224@code.launchpad.net |
Commit message
Description of the change
This branch adds a architecture field to the database and make the LP code add this architecture field when scanning.
This branch lacks database migration, so it will require the old db to be destroyed and a new one created unfortunately.
James Westby (james-w) wrote : | # |
- 65. By Michael Vogt
-
merged updated lp:~mvo/pkgme-devportal/raise-on-unsupported-architectures
- 66. By Michael Vogt
-
update update_
source_ package( ) for the new api - 67. By Michael Vogt
-
merged fix from lp:~mvo/pkgme-devportal/raise-on-unsupported-architectures
James Westby (james-w) wrote : | # |
Hi,
We'll want to add architecture to the query interface of libdep-service
at some point, but I was wrong that this will break libdep-service I think,
so approving.
Thanks,
James
James Westby (james-w) wrote : | # |
Oh, but we do need a schema migration to deploy this. We don't have a
framework for that yet.
We can probably do something cheap for this one if we have the SQL needed
to do the migration.
Thanks,
James
- 68. By Michael Vogt
-
merged from trunk to fix tests
James Westby (james-w) wrote : | # |
On Tue, 24 Jul 2012 16:01:18 -0000, James Westby <email address hidden> wrote:
> Oh, but we do need a schema migration to deploy this. We don't have a
> framework for that yet.
>
> We can probably do something cheap for this one if we have the SQL needed
> to do the migration.
To answer your question on IRC, it doesn't have to be a migration
per-se, but we can't rollout until we know how it is handled as all the
queries will break if they don't match the db, so we need an answer
before we can land this unfortunately.
Thanks,
James
Michael Vogt (mvo) wrote : | # |
Thanks James, so I assume we want something like the following sql:
"""
ALTER TABLE libdep ADD architecture TEXT;
UPDATE libdep SET architecture = "i386";
"""
to be run on the DB when this gets deployed. Would this be something we ask the webops to do ad-hoc?
Or something like https:/
Michael Vogt (mvo) wrote : | # |
Hm, apparently storm has some native support for this too, I look at this now.
Michael Vogt (mvo) wrote : | # |
Some info on this is here:
http://
but its not very well documented and it appears that it will not help with this migration as the DB itself needs to be created with the storm.schema stuff so that it has the "patch" table.
Michael Vogt (mvo) wrote : | # |
I put the storm version of this migration code into lp:~mvo/pkgme-devportal/schemas-via-storm now.
James Westby (james-w) wrote : | # |
Hi,
After discussing this more, the mechanism of the schema update isn't the important
thing, it's having the code deal with the fact that we won't do the migration
as well roll it out.
The steps would be
1) Land a change to cope with the architecture column being present or not:
- if it's there it should insert data in to it
- if it's there it can use it in queries, if it assumes that NULL == 'i386', or it can not use it in queries.
- it can add the column to the CREATE TABLE IF NOT EXIST statement, as that won't trigger when pushing out the code.
2) Roll that change out
3) Run SQL to ALTER TABLE ADD COLUMN architecture;
3) Run background job to UPDATE SET architecture = 'i386'; This may want to be batched.
4) Once the job completes ALTER COLUMN ADD CONSTRAINT NOT NULL;
5) The db is now ready, once this is done on production land a change to use the column in queries, expecting it to be there and with no nulls and 'i386' for all current data.
6) Roll that change out.
So, some changes are needed to this branch:
72 + "architecture TEXT, "
Should add a NOT NULL constraint I think, we expect every row to have a non-NULL architecture
eventually.
93 + result = self._store.
94 + (unicode(
It shouldn't change that currently, it should just assume that architecture == 'i386' for all rows for now.
108 + self._store.
109 + (unicode(
That needs to change the INSERT statement depending on whether the architecture column is present or not.
With those changes we can get this landed, rolled out to staging, and start the migration there.
Two things come to mind:
* A way to test if a column is part of a table will be important to have. jml has written code for this before I think.
* We may want something like dblooptuner for doing the background migration.
Thanks,
James
James Westby (james-w) wrote : | # |
lifeless corrected my process:
<lifeless> so for a given refactoring, like 'add a required column', you build that out of those two primitives:
<lifeless> - add a nullable column
<lifeless> - add a backfill job and make your other code populate the column, and handle it being null
<lifeless> - wait for the backfill
<lifeless> - change schema to make the column NOT NULL
<lifeless> done.
Thanks,
James
James Westby (james-w) wrote : | # |
Hi,
So we need a step before those because we currently do
"INSERT INTO libdep VALUES (?, ?, ?)"
which won't work with multiple columns I don't think (though
I haven't tested.) That means the first step would be to land
a change that altered that.
I don't want to block you on all of this though Michael, so I'd
be +1 on going ahead with a managed rollout of this change
with lockstep db migration, and to start to work on these
issues so we can do better with the next change.
Thanks,
James
James Westby (james-w) wrote : | # |
Oh, and I've just realised that I think architecture must be part of the
unique constraint, otherwise it will fail if the same package has the same
library and dependencies on multiple architectures.
Thanks,
James
Preview Diff
1 | === modified file 'acceptance/tests/__init__.py' |
2 | --- acceptance/tests/__init__.py 2012-07-17 12:34:24 +0000 |
3 | +++ acceptance/tests/__init__.py 2012-07-24 17:18:20 +0000 |
4 | @@ -53,8 +53,8 @@ |
5 | def test_gtk(self): |
6 | """Runs successfully for a basic GTK+ application.""" |
7 | dep_db = self.useFixture(DatabaseFixture()) |
8 | - dep_db.db.update_source_package("pthreads", [[("libpthread.so.0", "libpthread0")]]) |
9 | - dep_db.db.update_source_package("eglibc", [[("libc.so.6", "libc6")]]) |
10 | + dep_db.db.update_source_package("pthreads", [[("libpthread.so.0", "libpthread0", "i386")]]) |
11 | + dep_db.db.update_source_package("eglibc", [[("libc.so.6", "libc6", "i386")]]) |
12 | test_data = self.useFixture(TestData("gtk")) |
13 | self.run_pkgme(test_data) |
14 | self.assertThat( |
15 | @@ -63,7 +63,7 @@ |
16 | def test_bundled_library(self): |
17 | """Runs successfully for a basic bundled libary.""" |
18 | dep_db = self.useFixture(DatabaseFixture()) |
19 | - dep_db.db.update_source_package("eglibc", [[("libc.so.6", "libc6")]]) |
20 | + dep_db.db.update_source_package("eglibc", [[("libc.so.6", "libc6", "i386")]]) |
21 | test_data = self.useFixture(TestData("bundled_lib")) |
22 | self.run_pkgme(test_data) |
23 | self.assertThat( |
24 | |
25 | === modified file 'devportalbinary/binary.py' |
26 | --- devportalbinary/binary.py 2012-07-24 17:08:59 +0000 |
27 | +++ devportalbinary/binary.py 2012-07-24 17:18:20 +0000 |
28 | @@ -138,6 +138,7 @@ |
29 | cmd = [OBJDUMP, '-f', '-p'] + binary_paths |
30 | output = run_subprocess(cmd) |
31 | libraries = {} |
32 | + architecture = None |
33 | last_line_was_blank = True |
34 | for line in output: |
35 | this_line_blank = (line.strip() == '') |
36 | @@ -148,7 +149,8 @@ |
37 | break |
38 | libraries[current_binary] = [] |
39 | if line.startswith(' NEEDED'): |
40 | - libraries[current_binary].append(line.split()[1]) |
41 | + soname = line.split()[1] |
42 | + libraries[current_binary].append(soname) |
43 | elif line.startswith('architecture: '): |
44 | # so... i386 is reported as "i386" |
45 | # *but* amd64 as "i386:x86-64" |
46 | |
47 | === modified file 'devportalbinary/database.py' |
48 | --- devportalbinary/database.py 2012-07-19 17:29:48 +0000 |
49 | +++ devportalbinary/database.py 2012-07-24 17:18:20 +0000 |
50 | @@ -131,7 +131,7 @@ |
51 | def iter_symbols(self): |
52 | contents = self.get_symbol_contents() |
53 | if contents: |
54 | - yield contents |
55 | + yield contents, self.bpph.distro_arch_series.architecture_tag |
56 | |
57 | |
58 | class SourcePackage(object): |
59 | @@ -180,7 +180,7 @@ |
60 | path = download_file(url, directory) |
61 | symbols_contents = extract_symbols_file(path) |
62 | if symbols_contents: |
63 | - yield symbols_contents |
64 | + yield symbols_contents, architecture |
65 | shutil.rmtree(directory) |
66 | |
67 | |
68 | @@ -324,6 +324,7 @@ |
69 | "source_package_name TEXT, " |
70 | "library TEXT, " |
71 | "dependency TEXT, " |
72 | + "architecture TEXT, " |
73 | "CONSTRAINT libdep_uniq UNIQUE (source_package_name, " |
74 | "library, dependency))") |
75 | elif store_type == 'postgres': |
76 | @@ -337,6 +338,7 @@ |
77 | "source_package_name TEXT, " |
78 | "library TEXT, " |
79 | "dependency TEXT, " |
80 | + "architecture TEXT, " |
81 | "CONSTRAINT libdep_uniq UNIQUE (source_package_name, " |
82 | "library, dependency))") |
83 | else: |
84 | @@ -344,21 +346,22 @@ |
85 | store.commit() |
86 | return cls(store) |
87 | |
88 | - def get_dependencies(self, library_name): |
89 | + def get_dependencies(self, library_name, arch='i386'): |
90 | """Get the binary packagaes that provide 'library_name'.""" |
91 | - result = self._store.execute("SELECT dependency FROM libdep WHERE library = ?", |
92 | - (unicode(library_name),)) |
93 | + result = self._store.execute("SELECT dependency FROM libdep WHERE library = ? AND architecture = ?", |
94 | + (unicode(library_name), unicode(arch))) |
95 | return set([dependency for [dependency] in result]) |
96 | |
97 | - def insert_new_library(self, source_package_name, library_name, dependency): |
98 | + def insert_new_library(self, source_package_name, library_name, |
99 | + dependency, arch): |
100 | """Insert a library and its needed dependency into the database. |
101 | |
102 | :param library_name: A full soname, e.g. libfoo.so.1. |
103 | :param dependency: A binary package dependency, possibly including |
104 | version. |
105 | """ |
106 | - self._store.execute("INSERT INTO libdep VALUES (?, ?, ?)", |
107 | - (unicode(source_package_name), unicode(library_name), unicode(dependency))) |
108 | + self._store.execute("INSERT INTO libdep VALUES (?, ?, ?, ?)", |
109 | + (unicode(source_package_name), unicode(library_name), unicode(dependency), unicode(arch))) |
110 | |
111 | def update_source_package(self, source_package_name, symbols): |
112 | """Update the database with the symbols from 'source_package_name'. |
113 | @@ -372,9 +375,9 @@ |
114 | self._store.execute("DELETE FROM libdep WHERE source_package_name = ?", |
115 | (unicode(source_package_name),)) |
116 | for symbols in symbols: |
117 | - for library, dependency in symbols: |
118 | + for library, dependency, architecture in symbols: |
119 | self.insert_new_library( |
120 | - source_package_name, library, dependency) |
121 | + source_package_name, library, dependency, architecture) |
122 | self._store.commit() |
123 | |
124 | |
125 | @@ -389,8 +392,8 @@ |
126 | if not package: |
127 | return |
128 | symbols = [ |
129 | - list(iter_so_names(symbols)) |
130 | - for symbols in package.iter_symbols()] |
131 | + (list(iter_so_names(symbols)), architecture) |
132 | + for symbols, architecture in package.iter_symbols()] |
133 | db.update_source_package(package_name, symbols) |
134 | |
135 | |
136 | |
137 | === modified file 'devportalbinary/tests/test_binary.py' |
138 | --- devportalbinary/tests/test_binary.py 2012-07-24 15:05:27 +0000 |
139 | +++ devportalbinary/tests/test_binary.py 2012-07-24 17:18:20 +0000 |
140 | @@ -233,7 +233,7 @@ |
141 | |
142 | def test_guess_dependencies(self): |
143 | db = self.useFixture(DatabaseFixture()).db |
144 | - db.update_source_package('eglibc', [[('libc.so.6', 'libc6')]]) |
145 | + db.update_source_package('eglibc', [[('libc.so.6', 'libc6', 'i386')]]) |
146 | deps = guess_dependencies(get_test_data_dir_path('hello')) |
147 | self.assertEqual(set(['libc6']), deps) |
148 | |
149 | @@ -302,7 +302,7 @@ |
150 | shutil.copy( |
151 | get_test_data_file_path('hello', 'hello'), backend.path) |
152 | db = self.useFixture(DatabaseFixture()).db |
153 | - db.update_source_package('eglibc', [[('libc.so.6', 'libc6')]]) |
154 | + db.update_source_package('eglibc', [[('libc.so.6', 'libc6', 'i386')]]) |
155 | expected_deps = ', '.join(guess_dependencies(backend.path)) |
156 | build_deps = backend.get_build_depends(None) |
157 | self.assertEqual(expected_deps, build_deps) |
158 | @@ -337,8 +337,8 @@ |
159 | # The configuration file overrides the found library dependencies. |
160 | db = self.useFixture(DatabaseFixture()).db |
161 | db.update_source_package('foo', [ |
162 | - [('libasound.so.2', 'libfoo')], |
163 | - [('libasound.so.2', 'libbar')], |
164 | + [('libasound.so.2', 'libfoo', 'i386')], |
165 | + [('libasound.so.2', 'libbar', 'i386')], |
166 | ]) |
167 | self.assertEqual( |
168 | get_packages_for_libraries(["libasound.so.2"], "i386"), |
169 | |
170 | === modified file 'devportalbinary/tests/test_database.py' |
171 | --- devportalbinary/tests/test_database.py 2012-07-17 13:46:56 +0000 |
172 | +++ devportalbinary/tests/test_database.py 2012-07-24 17:18:20 +0000 |
173 | @@ -47,28 +47,28 @@ |
174 | |
175 | def test_insert_new_library(self): |
176 | db = self.get_package_db() |
177 | - db.insert_new_library('foo-src', 'libfoo.so.0', 'foo') |
178 | + db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386') |
179 | self.assertThat( |
180 | - "SELECT source_package_name, library, dependency FROM libdep", |
181 | - ResultsIn(db, [('foo-src', 'libfoo.so.0', 'foo')])) |
182 | + "SELECT source_package_name, library, dependency, architecture FROM libdep", |
183 | + ResultsIn(db, [('foo-src', 'libfoo.so.0', 'foo', 'i386')])) |
184 | |
185 | def test_double_insert(self): |
186 | db = self.get_package_db() |
187 | - db.insert_new_library('foo-src', 'libfoo.so.0', 'foo') |
188 | + db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386') |
189 | self.assertRaises( |
190 | IntegrityError, |
191 | - db.insert_new_library, 'foo-src', 'libfoo.so.0', 'foo') |
192 | + db.insert_new_library, 'foo-src', 'libfoo.so.0', 'foo', 'i386') |
193 | |
194 | def test_differing_dependencies(self): |
195 | db = self.get_package_db() |
196 | - db.insert_new_library('foo-src', 'libfoo.so.0', 'foo') |
197 | - db.insert_new_library('foo-src', 'libfoo.so.0', 'bar') |
198 | + db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386') |
199 | + db.insert_new_library('foo-src', 'libfoo.so.0', 'bar', 'i386') |
200 | deps = db.get_dependencies('libfoo.so.0') |
201 | self.assertEqual(deps, set(['foo', 'bar'])) |
202 | |
203 | def test_get_dependencies(self): |
204 | db = self.get_package_db() |
205 | - db.insert_new_library('foo-src', 'libfoo.so.0', 'foo') |
206 | + db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386') |
207 | deps = db.get_dependencies('libfoo.so.0') |
208 | self.assertEqual(deps, set(['foo'])) |
209 | |
210 | @@ -80,13 +80,13 @@ |
211 | def test_update_source_package(self): |
212 | db = self.get_package_db() |
213 | db.update_source_package( |
214 | - 'foo', [[('libfoo.so.1', 'foo-bin')]]) |
215 | + 'foo', [[('libfoo.so.1', 'foo-bin', 'i386')]]) |
216 | deps = db.get_dependencies('libfoo.so.1') |
217 | self.assertEqual(deps, set(['foo-bin'])) |
218 | |
219 | def test_update_existing_source_package_no_libraries(self): |
220 | db = self.get_package_db() |
221 | - db.update_source_package('foo', [[('libfoo.so.1', 'foo-bin')]]) |
222 | + db.update_source_package('foo', [[('libfoo.so.1', 'foo-bin', 'i386')]]) |
223 | # Run again, this time with no symbols, representing that a newer |
224 | # version of the package no longer exports any libraries. |
225 | db.update_source_package('foo', []) |
Hi Michael,
This looks sensible. It may be that we want a different schema for best querying,
but at this point I don't think so, and we'd need more information to know. Therefore
I think this is the right approach at this point.
Thanks,
James