Merge lp:~wgrant/launchpad/nu-overrides-adapters into lp:launchpad

Proposed by William Grant
Status: Merged
Merged at revision: 17138
Proposed branch: lp:~wgrant/launchpad/nu-overrides-adapters
Merge into: lp:launchpad
Diff against target: 358 lines (+95/-175)
3 files modified
lib/lp/archiveuploader/nascentupload.py (+89/-165)
lib/lp/soyuz/doc/soyuz-set-of-uploads.txt (+2/-1)
lib/lp/soyuz/scripts/packagecopier.py (+4/-9)
To merge this branch: bzr merge lp:~wgrant/launchpad/nu-overrides-adapters
Reviewer Review Type Date Requested Status
Celso Providelo (community) Approve
Review via email: mp+228619@code.launchpad.net

Commit message

Replace archiveuploader's custom override logic with calls into the common lp.soyuz.adapters.overrides implementation.

Description of the change

This branch replaces NacentUpload's override logic with calls into lp.soyuz.adapters.overrides, as already used by packagecopier and recently enhanced to be sufficient for archiveuploader.

There are some subtle functional changes here that bring archiveuploader into line with packagecopier. I've discussed them with Adam and Colin and they don't seem controversial.

 - The archive's latest publication in the series is used as the override ancestor, no longer filtering by pocket. Versions are still only checked against the upload pocket and RELEASE.

 - Resurrecting a deleted package with an upload will still hit NEW, but it will now default to the deleted publication's overrides.

 - Copy archive binaries are now overridden using exactly the normal primary archive rules, so they'll no longer land in contrib or non-free and get rejected.

To post a comment you must log in.
Revision history for this message
Celso Providelo (cprov) wrote :

Amazing how this new overriding mechanism is simplifying the code around it. Very nice design!

This change improves not only code readability but also correctness, great job.

As discussed on IRC, having proper overrides in NU will have good impacts in publications, possibly more cleanups (eg soyuz.model.publishing.get_component) and stop {S,B}PR component/section mutation (noted as XXX) and these effects will be explored in subsequent MPs.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archiveuploader/nascentupload.py'
2--- lib/lp/archiveuploader/nascentupload.py 2014-07-23 05:53:56 +0000
3+++ lib/lp/archiveuploader/nascentupload.py 2014-07-29 10:56:35 +0000
4@@ -46,10 +46,12 @@
5 from lp.services.librarian.interfaces import ILibraryFileAliasSet
6 from lp.soyuz.adapters.overrides import (
7 BinaryOverride,
8+ FallbackOverridePolicy,
9+ FromExistingOverridePolicy,
10 SourceOverride,
11- UnknownOverridePolicy,
12 )
13-from lp.soyuz.enums import PackagePublishingStatus
14+from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
15+from lp.soyuz.interfaces.component import IComponentSet
16 from lp.soyuz.interfaces.queue import QueueInconsistentStateError
17
18
19@@ -526,104 +528,12 @@
20 #
21 # Handling checking of versions and overrides
22 #
23- def getSourceAncestry(self, uploaded_file, archives, lookup_pockets):
24- """Return the last published source (ancestry) for a given file.
25-
26- Return the most recent ISPPH instance matching the uploaded file
27- package name or None.
28- """
29- for pocket in lookup_pockets:
30- for archive in archives:
31- candidates = archive.getPublishedSources(
32- name=uploaded_file.package, exact_match=True,
33- distroseries=self.policy.distroseries,
34- pocket=lookup_pockets,
35- status=(
36- PackagePublishingStatus.PUBLISHED,
37- PackagePublishingStatus.PENDING))
38- try:
39- return candidates[0]
40- except IndexError:
41- pass
42- return None
43-
44- def getBinaryAncestry(self, uploaded_file, archives, lookup_pockets):
45- """Return the last published binary (ancestry) for given file.
46-
47- Return the most recent IBPPH instance matching the uploaded file
48- package name or None.
49-
50- This method may raise NotFoundError if it is dealing with an
51- uploaded file targeted to an architecture not present in the
52- distroseries in context. So callsites needs to be aware.
53- """
54- # If we are dealing with a DDEB, use the DEB's overrides.
55- # If there's no deb_file set, don't worry about it. Rejection is
56- # already guaranteed.
57- if (isinstance(uploaded_file, DdebBinaryUploadFile)
58- and uploaded_file.deb_file):
59- ancestry_name = uploaded_file.deb_file.package
60- else:
61- ancestry_name = uploaded_file.package
62-
63- if uploaded_file.architecture == "all":
64- arch_indep = self.policy.distroseries.nominatedarchindep
65- archtag = arch_indep.architecturetag
66- else:
67- archtag = uploaded_file.architecture
68-
69- # XXX cprov 2007-02-13: it raises NotFoundError for unknown
70- # architectures. For now, it is treated in find_and_apply_overrides().
71- # But it should be refactored ASAP.
72- dar = self.policy.distroseries[archtag]
73-
74- for (arch, check_version) in (
75- (dar, True), (self.policy.distroseries.architectures, False)):
76- for archive in archives:
77- for pocket in lookup_pockets:
78- candidates = archive.getAllPublishedBinaries(
79- name=ancestry_name, exact_match=True,
80- distroarchseries=arch, pocket=pocket,
81- status=(
82- PackagePublishingStatus.PUBLISHED,
83- PackagePublishingStatus.PENDING))
84- if candidates:
85- return (candidates[0], check_version)
86- return None, None
87-
88- def _checkVersion(self, proposed_version, archive_version, filename):
89+ def checkVersion(self, proposed_version, archive_version, filename):
90 """Check if the proposed version is higher than the one in archive."""
91 if apt_pkg.version_compare(proposed_version, archive_version) < 0:
92 self.reject("%s: Version older than that in the archive. %s <= %s"
93 % (filename, proposed_version, archive_version))
94
95- def checkSourceVersion(self, uploaded_file, ancestry):
96- """Check if the uploaded source version is higher than the ancestry.
97-
98- Automatically mark the package as 'rejected' using _checkVersion().
99- """
100- # At this point DSC.version should be equal Changes.version.
101- # Anyway, we trust more in DSC.
102- proposed_version = self.changes.dsc.dsc_version
103- archive_version = ancestry.sourcepackagerelease.version
104- filename = uploaded_file.filename
105- self._checkVersion(proposed_version, archive_version, filename)
106-
107- def checkBinaryVersion(self, uploaded_file, ancestry):
108- """Check if the uploaded binary version is higher than the ancestry.
109-
110- Automatically mark the package as 'rejected' using _checkVersion().
111- """
112- # We only trust in the control version, specially because the
113- # 'version' from changesfile may not include epoch for binaries.
114- # This is actually something that needs attention in our buildfarm,
115- # because debuild does build the binary changesfile with a version
116- # that includes epoch.
117- proposed_version = uploaded_file.control_version
118- archive_version = ancestry.binarypackagerelease.version
119- filename = uploaded_file.filename
120- self._checkVersion(proposed_version, archive_version, filename)
121-
122 def overrideSourceFile(self, uploaded_file, override):
123 """Overrides the uploaded source based on its override information.
124
125@@ -654,9 +564,6 @@
126
127 Anything not yet in the DB gets tagged as 'new' and won't count
128 towards the permission check.
129-
130- XXX: wallyworld 2012-11-01 bug=1073755: This work should be done using
131- override polices defined in lp.soyuz.adapters.overrides.py
132 """
133 self.logger.debug("Finding and applying overrides.")
134
135@@ -668,54 +575,58 @@
136 if PackagePublishingPocket.RELEASE not in lookup_pockets:
137 lookup_pockets.append(PackagePublishingPocket.RELEASE)
138
139- archives = [self.policy.archive]
140- foreign_archive = False
141- use_default_component = True
142+ check_version = True
143 autoaccept_new = False
144- override_at_all = True
145- if self.policy.archive.is_partner:
146- use_default_component = False
147- elif self.policy.archive.is_copy:
148+ if self.policy.archive.is_copy:
149 # Copy archives always inherit their overrides from the
150 # primary archive. We don't want to perform the version
151 # check in this case, as the rebuild may finish after a new
152 # version exists in the primary archive.
153- archives = [self.policy.archive.distribution.main_archive]
154- foreign_archive = True
155- # XXX wgrant 2014-07-14 bug=1103491: This causes new binaries in
156- # copy archives to stay in contrib/non-free, so the upload gets
157- # rejected. But I'm just preserving existing behaviour for now.
158- use_default_component = False
159+ check_version = False
160 autoaccept_new = True
161 elif self.policy.archive.is_ppa:
162 autoaccept_new = True
163- override_at_all = False
164+
165+ override_policy = self.policy.archive.getOverridePolicy(
166+ self.policy.distroseries, self.policy.pocket)
167+
168+ if check_version:
169+ version_policy = FallbackOverridePolicy([
170+ FromExistingOverridePolicy(
171+ self.policy.archive, self.policy.distroseries, pocket)
172+ for pocket in lookup_pockets])
173+ else:
174+ version_policy = None
175
176 for uploaded_file in self.changes.files:
177+ upload_component = getUtility(IComponentSet)[
178+ uploaded_file.component_name]
179 if isinstance(uploaded_file, DSCFile):
180 self.logger.debug(
181 "Checking for %s/%s source ancestry"
182 % (uploaded_file.package, uploaded_file.version))
183- ancestry = self.getSourceAncestry(
184- uploaded_file, archives, lookup_pockets)
185- if ancestry is not None:
186- self.logger.debug("%s (source) exists in %s" % (
187- ancestry.sourcepackagerelease.title,
188- ancestry.pocket.name))
189- self.checkSourceVersion(uploaded_file, ancestry)
190- else:
191- if not autoaccept_new:
192- self.logger.debug(
193- "%s: (source) NEW", uploaded_file.package)
194- uploaded_file.new = True
195- if use_default_component:
196- ancestry = SourceOverride(
197- component=(
198- UnknownOverridePolicy.getComponentOverride(
199- uploaded_file.component_name,
200- return_component=True)))
201- if override_at_all and ancestry is not None:
202- self.overrideSourceFile(uploaded_file, ancestry)
203+ spn = getUtility(ISourcePackageNameSet).getOrCreateByName(
204+ uploaded_file.package)
205+ override = override_policy.calculateSourceOverrides(
206+ {spn: SourceOverride(component=upload_component)})[spn]
207+ if version_policy is not None:
208+ ancestor = version_policy.calculateSourceOverrides(
209+ {spn: SourceOverride()}).get(spn)
210+ if ancestor is not None and ancestor.version is not None:
211+ self.checkVersion(
212+ self.changes.dsc.dsc_version, ancestor.version,
213+ uploaded_file.filename)
214+ if override.new != False and not autoaccept_new:
215+ self.logger.debug(
216+ "%s: (source) NEW", uploaded_file.package)
217+ uploaded_file.new = True
218+ # XXX wgrant 2014-07-23: We want to preserve the upload
219+ # component for PPA uploads, so we force the component
220+ # to main when we create publications later. Eventually
221+ # we should never mutate the SPR/BPR here, and just
222+ # store a dict of overrides.
223+ if not self.policy.archive.is_ppa:
224+ self.overrideSourceFile(uploaded_file, override)
225 elif isinstance(uploaded_file, BaseBinaryUploadFile):
226 self.logger.debug(
227 "Checking for %s/%s/%s binary ancestry"
228@@ -724,39 +635,52 @@
229 uploaded_file.version,
230 uploaded_file.architecture,
231 ))
232- # Find the best ancestor publication for this binary. If
233- # it's from this archive and architecture we also want
234- # to make sure the version isn't going backwards.
235- ancestry, check_version = self.getBinaryAncestry(
236- uploaded_file, archives, lookup_pockets)
237- if ancestry is not None:
238- self.logger.debug("%s (binary) exists in %s/%s" % (
239- ancestry.binarypackagerelease.title,
240- ancestry.distroarchseries.architecturetag,
241- ancestry.pocket.name))
242- if check_version and not foreign_archive:
243- self.checkBinaryVersion(uploaded_file, ancestry)
244- else:
245- if not autoaccept_new:
246- self.logger.debug(
247- "%s: (binary) NEW", uploaded_file.package)
248- uploaded_file.new = True
249- # Check the current source publication's component.
250- # If there is a corresponding source publication, we will
251- # use the component from that, otherwise default mappings
252- # are used.
253- try:
254- spph = uploaded_file.findCurrentSourcePublication()
255- ancestry = BinaryOverride(component=spph.component)
256- except UploadError:
257- pass
258- if ancestry is None and use_default_component:
259- ancestry = BinaryOverride(
260- component=UnknownOverridePolicy.getComponentOverride(
261- uploaded_file.component_name,
262- return_component=True))
263- if override_at_all and ancestry is not None:
264- self.overrideBinaryFile(uploaded_file, ancestry)
265+
266+ # If we are dealing with a DDEB, use the DEB's
267+ # overrides. If there's no deb_file set, don't worry
268+ # about it. Rejection is already guaranteed.
269+ if (isinstance(uploaded_file, DdebBinaryUploadFile)
270+ and uploaded_file.deb_file):
271+ override_name = uploaded_file.deb_file.package
272+ else:
273+ override_name = uploaded_file.package
274+ bpn = getUtility(IBinaryPackageNameSet).getOrCreateByName(
275+ override_name)
276+
277+ if uploaded_file.architecture == "all":
278+ archtag = None
279+ else:
280+ archtag = uploaded_file.architecture
281+
282+ try:
283+ spph = uploaded_file.findCurrentSourcePublication()
284+ source_override = SourceOverride(component=spph.component)
285+ except UploadError:
286+ source_override = None
287+
288+ override = override_policy.calculateBinaryOverrides(
289+ {(bpn, archtag): BinaryOverride(
290+ component=upload_component,
291+ source_override=source_override)})[(bpn, archtag)]
292+ if version_policy is not None:
293+ ancestor = version_policy.calculateBinaryOverrides(
294+ {(bpn, archtag): BinaryOverride(
295+ component=upload_component)}).get((bpn, archtag))
296+ if ancestor is not None and ancestor.version is not None:
297+ self.checkVersion(
298+ uploaded_file.control_version, ancestor.version,
299+ uploaded_file.filename)
300+ if override.new != False and not autoaccept_new:
301+ self.logger.debug(
302+ "%s: (binary) NEW", uploaded_file.package)
303+ uploaded_file.new = True
304+ # XXX wgrant 2014-07-23: We want to preserve the upload
305+ # component for PPA uploads, so we force the component
306+ # to main when we create publications later. Eventually
307+ # we should never mutate the SPR/BPR here, and just
308+ # store a dict of overrides.
309+ if not self.policy.archive.is_ppa:
310+ self.overrideBinaryFile(uploaded_file, override)
311
312 #
313 # Actually processing accepted or rejected uploads -- and mailing people
314
315=== modified file 'lib/lp/soyuz/doc/soyuz-set-of-uploads.txt'
316--- lib/lp/soyuz/doc/soyuz-set-of-uploads.txt 2014-07-09 05:37:40 +0000
317+++ lib/lp/soyuz/doc/soyuz-set-of-uploads.txt 2014-07-29 10:56:35 +0000
318@@ -529,7 +529,8 @@
319 ... loglevel=logging.DEBUG)
320 DEBUG ...
321 DEBUG Checking for foo/2.9-1/powerpc binary ancestry
322- DEBUG foo-1.0-1 (binary) exists in i386/RELEASE
323+ ...
324+ DEBUG Setting it to ACCEPTED
325 ...
326 Upload complete.
327
328
329=== modified file 'lib/lp/soyuz/scripts/packagecopier.py'
330--- lib/lp/soyuz/scripts/packagecopier.py 2014-07-29 07:29:32 +0000
331+++ lib/lp/soyuz/scripts/packagecopier.py 2014-07-29 10:56:35 +0000
332@@ -174,7 +174,7 @@
333 spn = source.sourcepackagerelease.sourcepackagename
334 policy = archive.getOverridePolicy(dest_series, pocket)
335 override = policy.calculateSourceOverrides(
336- {spn: SourceOverride(component=source.component)}).get(spn)
337+ {spn: SourceOverride(component=source.component)})[spn]
338
339 # Is the destination pocket open at all?
340 reason = archive.checkUploadToPocket(
341@@ -698,14 +698,9 @@
342 if override is None and policy is not None:
343 # Only one override can be returned so take the first
344 # element of the returned list.
345- overrides = policy.calculateSourceOverrides(
346- {source.sourcepackagerelease.sourcepackagename:
347- SourceOverride()})
348- # Only one override can be returned so take the first
349- # element of the returned list.
350- assert len(overrides) == 1, (
351- "More than one override encountered, something is wrong.")
352- override = overrides[source.sourcepackagerelease.sourcepackagename]
353+ override = policy.calculateSourceOverrides(
354+ {source.sourcepackagerelease.sourcepackagename: SourceOverride()}
355+ )[source.sourcepackagerelease.sourcepackagename]
356 if source_in_destination.is_empty():
357 # If no manual overrides were specified and the archive has an
358 # override policy then use that policy to get overrides.