Merge lp:~wgrant/launchpad/bug-680889-arch-wildcards into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: Henning Eggers
Approved revision: no longer in the source branch.
Merged at revision: 12029
Proposed branch: lp:~wgrant/launchpad/bug-680889-arch-wildcards
Merge into: lp:launchpad
Diff against target: 291 lines (+121/-131)
3 files modified
lib/lp/soyuz/doc/package-arch-specific.txt (+0/-116)
lib/lp/soyuz/pas.py (+22/-15)
lib/lp/soyuz/tests/test_pas.py (+99/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/bug-680889-arch-wildcards
Reviewer Review Type Date Requested Status
Henning Eggers (community) code Approve
Review via email: mp+42723@code.launchpad.net

Commit message

[r=henninge][ui=none][bug=680889] Complex architecture hint strings involving architecture wildcards (e.g. "all linux-any") are now handled correctly.

Description of the change

As reported in bug #680889, lp.soyuz.pas.determineArchitecturesToBuild fails to properly handle valid architecture hint strings such as "all linux-any", creating no builds at all. This branch adjusts the "all", "any", and "linux-any" special cases to match even when they are not the sole component of the string.

It's a pretty simple change, except that I had to move the "all" check to the end, so hint strings like "all amd64" create an amd64 build, not an i386 arch-indep one.

I extended the tests to cover the specific problem reported and a few other edge cases.

To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :

Hi William,
thanks for this fix. The code looks good to me with my limited domain knowledge. ;-) I am worried about the doc test growing out of proportion, though, as it covers more corner cases. I would think these should be tested in a unit test while the doctest just shows the general operation of the code. Could you do something along those lines, please?
Cheers,
Henning

review: Needs Fixing (code)
Revision history for this message
Henning Eggers (henninge) wrote :

Thanks, great work, enormous improvement!

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/doc/package-arch-specific.txt'
2--- lib/lp/soyuz/doc/package-arch-specific.txt 2010-10-14 18:42:19 +0000
3+++ lib/lp/soyuz/doc/package-arch-specific.txt 2010-12-08 13:37:07 +0000
4@@ -26,43 +26,10 @@
5 >>> ignore = test_publisher.setUpDefaultDistroSeries(hoary)
6 >>> test_publisher.addFakeChroots()
7
8-Create an architecture-independent source.
9-
10- >>> pub_all = test_publisher.getPubSource(
11- ... sourcename='test-buildd', version='667')
12-
13-Create an architecture-specific source.
14-
15- >>> pub_any = test_publisher.getPubSource(
16- ... sourcename='test-buildd-2', version='668',
17- ... architecturehintlist="any")
18-
19-Create a source with a specific list of architectures to be built.
20-
21 >>> pub_three = test_publisher.getPubSource(
22 ... sourcename='test-buildd-3', version='669',
23 ... architecturehintlist="i386 hppa amd64")
24
25-Create a source with one single specific architecture to be built.
26-
27- >>> pub_one = test_publisher.getPubSource(
28- ... sourcename='test-buildd-4', version='670',
29- ... architecturehintlist="hppa")
30-
31-Create sources with 'any-$(ARCH)', 'linux-$(ARCH)' and 'linux-any' notation.
32-
33- >>> pub_any_foo = test_publisher.getPubSource(
34- ... sourcename='test-buildd-6', version='672',
35- ... architecturehintlist="any-hppa")
36-
37- >>> pub_linux_foo = test_publisher.getPubSource(
38- ... sourcename='test-buildd-7', version='673',
39- ... architecturehintlist="linux-i386")
40-
41- >>> pub_linux_any = test_publisher.getPubSource(
42- ... sourcename='test-buildd-7', version='674',
43- ... architecturehintlist="linux-any")
44-
45 Create a PPA source publication.
46
47 >>> from lp.registry.interfaces.person import IPersonSet
48@@ -107,89 +74,6 @@
49 ... pub, legal_archs, hoary, pas_verify)
50 ... print_architectures(allowed_architectures)
51
52- >>> print_build_architectures(pub_all)
53- i386
54-
55- >>> print_build_architectures(pub_any)
56- hppa
57- i386
58-
59- >>> print_build_architectures(pub_three)
60- hppa
61- i386
62-
63- >>> print_build_architectures(pub_one)
64- hppa
65-
66-If an architecture is disabled for some reason, then the results from
67-determineArchitecturesToBuild() will not include it.
68-
69- >>> hoary['hppa'].enabled = False
70-
71- >>> print_build_architectures(pub_three)
72- i386
73-
74-Re-enable it before continuing:
75- >>> hoary['hppa'].enabled = True
76-
77-
78-== Check support for kernel notation in architecture hint list ==
79-
80-dpkg supports the use of the '<kernel-><arch>' notation in the
81-'Architecture' field, where 'kernel-' is one of the kernel platform
82-available in debian systems: ['linux-', 'kfreebsd', 'hurd-', 'any-']
83-
84-See bug 73761 and http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=268709
85-for further information about this dpkg feature.
86-
87-Soyuz will consider only 'any-' and 'linux-' elements as the
88-corresponding architecture, since only linux is supported in Ubuntu.
89-
90- >>> print_build_architectures(pub_any_foo)
91- hppa
92-
93- >>> print_build_architectures(pub_linux_foo)
94- i386
95-
96- >>> print_build_architectures(pub_linux_any)
97- hppa
98- i386
99-
100-
101-== PPA architectures ==
102-
103-For virtualized archives only architectures with established support for
104-PPA will be considered when creating PPA builds. See distroarchseries.txt
105-for further information.
106-
107- >>> ppa_architectures = [
108- ... arch for arch in hoary.architectures if arch.supports_virtualized]
109- >>> print_architectures(ppa_architectures)
110- i386
111-
112- >>> print_build_architectures(pub_ppa)
113- i386
114-
115-Conversely, for non-virtual archives, build records are created for all
116-architectures supported for the distroseries.
117-
118- >>> LaunchpadZopelessLayer.switchDbUser("launchpad")
119- >>> pub_ppa.archive.require_virtualized = False
120- >>> commit()
121- >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
122-
123- >>> print_build_architectures(pub_ppa)
124- hppa
125- i386
126-
127-Revert the PPA changes.
128-
129- >>> LaunchpadZopelessLayer.switchDbUser("launchpad")
130- >>> pub_ppa.archive.require_virtualized = True
131- >>> commit()
132- >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
133-
134-
135 === Source PAS ===
136
137 Source PAS lines are the ones starting with '%' followed by the source
138
139=== modified file 'lib/lp/soyuz/pas.py'
140--- lib/lp/soyuz/pas.py 2010-10-06 11:46:51 +0000
141+++ lib/lp/soyuz/pas.py 2010-12-08 13:37:07 +0000
142@@ -167,23 +167,30 @@
143 legal_arch_tags = set(
144 arch.architecturetag for arch in legal_archseries if arch.enabled)
145
146- # We need to support arch tags like any-foo and linux-foo, so remove
147- # supported kernel prefixes, Also allow linux-any but not any-any.
148- # See bugs #73761 and #605002.
149- if hint_string in ['any', 'linux-any']:
150+ hint_archs = set(hint_string.split())
151+
152+ # If a *-any architecture wildcard is present, build for everything
153+ # we can. We only support Linux-based architectures at the moment,
154+ # and any-any isn't a valid wildcard. See bug #605002.
155+ if hint_archs.intersection(('any', 'linux-any')):
156 package_tags = legal_arch_tags
157- elif hint_string == 'all':
158- nominated_arch = distroseries.nominatedarchindep
159- legal_archseries_ids = [arch.id for arch in legal_archseries]
160- assert nominated_arch.id in legal_archseries_ids, (
161- 'nominatedarchindep is not present in legal_archseries: %s' %
162- ' '.join(legal_arch_tags))
163- package_tags = set([nominated_arch.architecturetag])
164 else:
165- my_archs = hint_string.split()
166- for kernel in ['linux', 'any']:
167- my_archs = [arch.replace("%s-" % kernel, "") for arch in my_archs]
168- package_tags = set(my_archs).intersection(legal_arch_tags)
169+ # We need to support arch tags like any-foo and linux-foo, so remove
170+ # supported kernel prefixes. See bug #73761.
171+ stripped_archs = hint_archs
172+ for kernel in ('linux', 'any'):
173+ stripped_archs = set(
174+ arch.replace("%s-" % kernel, "") for arch in stripped_archs)
175+ package_tags = stripped_archs.intersection(legal_arch_tags)
176+
177+ # 'all' is only used as a last resort, to create an arch-indep
178+ # build where no builds would otherwise exist.
179+ if len(package_tags) == 0 and 'all' in hint_archs:
180+ nominated_arch = distroseries.nominatedarchindep
181+ assert nominated_arch in legal_archseries, (
182+ 'nominatedarchindep is not present in legal_archseries: %s' %
183+ ' '.join(legal_arch_tags))
184+ package_tags = set([nominated_arch.architecturetag])
185
186 if pas_verify:
187 build_tags = set()
188
189=== added file 'lib/lp/soyuz/tests/test_pas.py'
190--- lib/lp/soyuz/tests/test_pas.py 1970-01-01 00:00:00 +0000
191+++ lib/lp/soyuz/tests/test_pas.py 2010-12-08 13:37:07 +0000
192@@ -0,0 +1,99 @@
193+# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
194+# GNU Affero General Public License version 3 (see the file LICENSE).
195+
196+from canonical.testing.layers import LaunchpadZopelessLayer
197+from lp.soyuz.pas import determineArchitecturesToBuild
198+from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
199+from lp.testing import TestCaseWithFactory
200+
201+
202+class TestDetermineArchitecturesToBuild(TestCaseWithFactory):
203+ """Test that determineArchitecturesToBuild correctly interprets hints."""
204+
205+ layer = LaunchpadZopelessLayer
206+
207+ def setUp(self):
208+ super(TestDetermineArchitecturesToBuild, self).setUp()
209+ self.publisher = SoyuzTestPublisher()
210+ self.publisher.prepareBreezyAutotest()
211+ self.publisher.addFakeChroots()
212+
213+ def assertArchsForHint(self, hint_string, expected_arch_tags):
214+ """Assert that the given hint resolves to the expected archtags."""
215+ pub = self.publisher.getPubSource(architecturehintlist=hint_string)
216+ architectures = determineArchitecturesToBuild(
217+ pub, self.publisher.breezy_autotest.architectures,
218+ self.publisher.breezy_autotest)
219+ self.assertEqual(
220+ expected_arch_tags, [a.architecturetag for a in architectures])
221+
222+ def test_single_architecture(self):
223+ # A hint string with a single arch resolves to just that arch.
224+ self.assertArchsForHint('hppa', ['hppa'])
225+
226+ def test_three_architectures(self):
227+ # A hint string with multiple archs resolves to just those
228+ # archs.
229+ self.assertArchsForHint('amd64 i386 hppa', ['hppa', 'i386'])
230+
231+ def test_independent(self):
232+ # 'all' is special, meaning just a single build. The
233+ # nominatedarchindep architecture is used -- in this case i386.
234+ self.assertArchsForHint('all', ['i386'])
235+
236+ def test_one_and_independent(self):
237+ # 'all' is redundant if we have another build anyway.
238+ self.assertArchsForHint('hppa all', ['hppa'])
239+
240+ def test_fictional_and_independent(self):
241+ # But 'all' is useful if present with an arch that wouldn't
242+ # generate a build.
243+ self.assertArchsForHint('foo all', ['i386'])
244+
245+ def test_wildcard(self):
246+ # 'any' is a wildcard that matches all available archs.
247+ self.assertArchsForHint('any', ['hppa', 'i386'])
248+
249+ def test_kernel_specific_architecture(self):
250+ # Since we only support Linux-based architectures, 'linux-foo'
251+ # is treated the same as 'foo'.
252+ self.assertArchsForHint('linux-hppa', ['hppa'])
253+
254+ def test_unknown_kernel_specific_architecture(self):
255+ # Non-Linux architectures aren't supported.
256+ self.assertArchsForHint('kfreebsd-hppa', [])
257+
258+ def test_kernel_wildcard_architecture(self):
259+ # Wildcards work for kernels: 'any-foo' is treated like 'foo'.
260+ self.assertArchsForHint('any-hppa', ['hppa'])
261+
262+ def test_kernel_specific_architecture_wildcard(self):
263+ # Wildcards work for archs too: 'linux-any' is treated like 'any'.
264+ self.assertArchsForHint('linux-any', ['hppa', 'i386'])
265+
266+ def test_unknown_kernel_specific_architecture_wildcard(self):
267+ # But unknown kernels continue to result in nothing.
268+ self.assertArchsForHint('kfreebsd-any', [])
269+
270+ def test_wildcard_and_independent(self):
271+ # 'all' continues to be ignored alongside a valid wildcard.
272+ self.assertArchsForHint('all linux-any', ['hppa', 'i386'])
273+
274+ def test_kernel_independent_is_invalid(self):
275+ # 'linux-all' isn't supported.
276+ self.assertArchsForHint('linux-all', [])
277+
278+ def test_double_wildcard_is_invalid(self):
279+ # 'any-any' is invalid; you want 'any'.
280+ self.assertArchsForHint('any-any', [])
281+
282+ def test_disabled_architectures_omitted(self):
283+ # Disabled architectures are not buildable, so are excluded.
284+ self.publisher.breezy_autotest['hppa'].enabled = False
285+ self.assertArchsForHint('any', ['i386'])
286+
287+ def test_virtualized_archives_have_only_virtualized_archs(self):
288+ # For archives which must build on virtual builders, only
289+ # virtual archs are returned.
290+ self.publisher.breezy_autotest.main_archive.require_virtualized = True
291+ self.assertArchsForHint('any', ['i386'])