Merge lp:~cjwatson/launchpad/by-hash-prune-immutable into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18012
Proposed branch: lp:~cjwatson/launchpad/by-hash-prune-immutable
Merge into: lp:launchpad
Diff against target: 240 lines (+82/-48)
2 files modified
lib/lp/archivepublisher/publishing.py (+39/-31)
lib/lp/archivepublisher/tests/test_publisher.py (+43/-17)
To merge this branch: bzr merge lp:~cjwatson/launchpad/by-hash-prune-immutable
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+293145@code.launchpad.net

Commit message

Prune by-hash files from immutable suites without trying to regenerate the Release file.

Description of the change

Prune by-hash files from immutable suites without trying to regenerate the Release file. This fixes OOPSes we saw following the 16.04 release, and for which we currently have a somewhat different cowboy in place (https://pastebin.canonical.com/155032/).

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
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/archivepublisher/publishing.py'
2--- lib/lp/archivepublisher/publishing.py 2016-04-14 10:26:19 +0000
3+++ lib/lp/archivepublisher/publishing.py 2016-04-29 11:07:52 +0000
4@@ -758,18 +758,14 @@
5 distroseries, pocket = self.distro.getDistroSeriesAndPocket(
6 container[len(u"release:"):])
7 archive_file_suites.add((distroseries, pocket))
8- self.release_files_needed.add((distroseries.name, pocket))
9
10 for distroseries in self.distro:
11 for pocket in self.archive.getPockets():
12- if not is_careful:
13- if (not self.isDirty(distroseries, pocket) and
14- (distroseries, pocket) not in archive_file_suites):
15- self.log.debug("Skipping release files for %s/%s" %
16- (distroseries.name, pocket.name))
17- continue
18- self.checkDirtySuiteBeforePublishing(distroseries, pocket)
19- else:
20+ suite = distroseries.getSuite(pocket)
21+ release_path = os.path.join(
22+ self._config.distsroot, suite, "Release")
23+
24+ if is_careful:
25 if not self.isAllowed(distroseries, pocket):
26 continue
27 # If we were asked for careful Release file generation
28@@ -777,13 +773,25 @@
29 # material needed to generate Release files for all
30 # suites. Only force those suites that already have
31 # Release files.
32- release_path = os.path.join(
33- self._config.distsroot, distroseries.getSuite(pocket),
34- "Release")
35 if file_exists(release_path):
36 self.release_files_needed.add(
37 (distroseries.name, pocket))
38- self._writeSuite(distroseries, pocket)
39+
40+ if (distroseries.name, pocket) in self.release_files_needed:
41+ if not is_careful:
42+ if not self.isDirty(distroseries, pocket):
43+ self.log.debug("Skipping release files for %s/%s" %
44+ (distroseries.name, pocket.name))
45+ continue
46+ self.checkDirtySuiteBeforePublishing(
47+ distroseries, pocket)
48+ self._writeSuite(distroseries, pocket)
49+ elif ((distroseries, pocket) in archive_file_suites and
50+ distroseries.publish_by_hash):
51+ # We aren't publishing a new Release file for this
52+ # suite, probably because it's immutable, but we still
53+ # need to prune by-hash files from it.
54+ self._updateByHash(suite, "Release")
55
56 def _allIndexFiles(self, distroseries):
57 """Return all index files on disk for a distroseries.
58@@ -1005,7 +1013,7 @@
59 return self.distro.displayname
60 return "LP-PPA-%s" % get_ppa_reference(self.archive)
61
62- def _updateByHash(self, suite, release_data):
63+ def _updateByHash(self, suite, release_file_name):
64 """Update by-hash files for a suite.
65
66 This takes Release file data which references a set of on-disk
67@@ -1014,6 +1022,10 @@
68 directories to be in sync with ArchiveFile. Any on-disk by-hash
69 entries that ceased to be current sufficiently long ago are removed.
70 """
71+ release_path = os.path.join(
72+ self._config.distsroot, suite, release_file_name)
73+ with open(release_path) as release_file:
74+ release_data = Release(release_file)
75 archive_file_set = getUtility(IArchiveFileSet)
76 by_hashes = ByHashes(self._config.distsroot, self.log)
77 suite_dir = os.path.relpath(
78@@ -1032,7 +1044,7 @@
79 for current_entry in release_data["SHA256"]:
80 path = os.path.join(suite_dir, current_entry["name"])
81 current_files[path] = (
82- current_entry["size"], current_entry["sha256"])
83+ int(current_entry["size"]), current_entry["sha256"])
84 current_sha256_checksums.add(current_entry["sha256"])
85 uncondemned_files = set()
86 for db_file in archive_file_set.getByArchive(
87@@ -1108,14 +1120,15 @@
88 by_hashes.prune()
89
90 def _writeReleaseFile(self, suite, release_data):
91- """Write a Release file to the archive.
92+ """Write a Release file to the archive (as Release.new).
93
94 :param suite: The name of the suite whose Release file is to be
95 written.
96 :param release_data: A `debian.deb822.Release` object to write
97 to the filesystem.
98 """
99- release_path = os.path.join(self._config.distsroot, suite, "Release")
100+ release_path = os.path.join(
101+ self._config.distsroot, suite, "Release.new")
102 with open_for_writing(release_path, "w") as release_file:
103 release_data.dump(release_file, "utf-8")
104
105@@ -1131,16 +1144,6 @@
106 def _writeSuite(self, distroseries, pocket):
107 """Write out the Release files for the provided suite."""
108 # XXX: kiko 2006-08-24: Untested method.
109-
110- # As we generate file lists for apt-ftparchive we record which
111- # distroseriess and so on we need to generate Release files for.
112- # We store this in release_files_needed and consume the information
113- # when writeReleaseFiles is called.
114- if (distroseries.name, pocket) not in self.release_files_needed:
115- # If we don't need to generate a release for this release
116- # and pocket, don't!
117- return
118-
119 suite = distroseries.getSuite(pocket)
120 suite_dir = os.path.join(self._config.distsroot, suite)
121 all_components = [
122@@ -1219,14 +1222,19 @@
123 release_file.setdefault(archive_hash.apt_name, []).append(
124 hashes[archive_hash.deb822_name])
125
126- if distroseries.publish_by_hash:
127- self._updateByHash(suite, release_file)
128- if distroseries.advertise_by_hash:
129- release_file["Acquire-By-Hash"] = "yes"
130+ if distroseries.publish_by_hash and distroseries.advertise_by_hash:
131+ release_file["Acquire-By-Hash"] = "yes"
132
133 self._writeReleaseFile(suite, release_file)
134 core_files.add("Release")
135
136+ if distroseries.publish_by_hash:
137+ self._updateByHash(suite, "Release.new")
138+
139+ os.rename(
140+ os.path.join(suite_dir, "Release.new"),
141+ os.path.join(suite_dir, "Release"))
142+
143 if self.archive.signing_key is not None:
144 # Sign the repository.
145 self.log.debug("Signing Release file for %s" % suite)
146
147=== modified file 'lib/lp/archivepublisher/tests/test_publisher.py'
148--- lib/lp/archivepublisher/tests/test_publisher.py 2016-04-14 10:26:19 +0000
149+++ lib/lp/archivepublisher/tests/test_publisher.py 2016-04-29 11:07:52 +0000
150@@ -2797,9 +2797,7 @@
151 MatchesStructure(scheduled_deletion_date=Not(Is(None))),
152 ]))
153
154- def test_prune(self):
155- # The publisher prunes files from by-hash that were condemned more
156- # than a day ago.
157+ def setUpPruneableSuite(self):
158 self.breezy_autotest.publish_by_hash = True
159 self.breezy_autotest.advertise_by_hash = True
160 publisher = Publisher(
161@@ -2843,16 +2841,44 @@
162 suite_path('main', 'source', 'by-hash'),
163 Not(ByHashHasContents(main_contents)))
164
165- # Use a fresh Publisher instance to ensure that it doesn't have
166- # dirty-pocket state left over from the last run.
167- publisher = Publisher(
168- self.logger, self.config, self.disk_pool,
169- self.ubuntutest.main_archive)
170- self.runSteps(publisher, step_a2=True, step_c=True, step_d=True)
171- self.assertEqual(set(), publisher.dirty_pockets)
172- self.assertContentEqual(
173- [('breezy-autotest', PackagePublishingPocket.RELEASE)],
174- publisher.release_files_needed)
175+ return main_contents
176+
177+ def test_prune(self):
178+ # The publisher prunes files from by-hash that were condemned more
179+ # than a day ago.
180+ main_contents = self.setUpPruneableSuite()
181+ suite_path = partial(
182+ os.path.join, self.config.distsroot, 'breezy-autotest')
183+
184+ # Use a fresh Publisher instance to ensure that it doesn't have
185+ # dirty-pocket state left over from the last run.
186+ publisher = Publisher(
187+ self.logger, self.config, self.disk_pool,
188+ self.ubuntutest.main_archive)
189+ self.runSteps(publisher, step_a2=True, step_c=True, step_d=True)
190+ self.assertEqual(set(), publisher.dirty_pockets)
191+ self.assertThat(
192+ suite_path('main', 'source', 'by-hash'),
193+ ByHashHasContents(main_contents))
194+
195+ def test_prune_immutable(self):
196+ # The publisher prunes by-hash files from immutable suites, but
197+ # doesn't regenerate the Release file in that case.
198+ main_contents = self.setUpPruneableSuite()
199+ suite_path = partial(
200+ os.path.join, self.config.distsroot, 'breezy-autotest')
201+ release_path = suite_path('Release')
202+ release_mtime = os.stat(release_path).st_mtime
203+
204+ self.breezy_autotest.status = SeriesStatus.CURRENT
205+ # Use a fresh Publisher instance to ensure that it doesn't have
206+ # dirty-pocket state left over from the last run.
207+ publisher = Publisher(
208+ self.logger, self.config, self.disk_pool,
209+ self.ubuntutest.main_archive)
210+ self.runSteps(publisher, step_a2=True, step_c=True, step_d=True)
211+ self.assertEqual(set(), publisher.dirty_pockets)
212+ self.assertEqual(release_mtime, os.stat(release_path).st_mtime)
213 self.assertThat(
214 suite_path('main', 'source', 'by-hash'),
215 ByHashHasContents(main_contents))
216@@ -3078,13 +3104,13 @@
217 releases_dir = self.getReleaseFileDir(root, series, suite)
218 os.makedirs(releases_dir)
219 release_data = self.makeFakeReleaseData()
220- release_path = os.path.join(releases_dir, "Release")
221+ release_path = os.path.join(releases_dir, "Release.new")
222
223 self.makePublisher(series)._writeReleaseFile(suite, release_data)
224
225 self.assertTrue(file_exists(release_path))
226- self.assertEqual(
227- release_data.encode('utf-8'), file(release_path).read())
228+ with open(release_path) as release_file:
229+ self.assertEqual(release_data.encode('utf-8'), release_file.read())
230
231 def test_writeReleaseFile_creates_directory_if_necessary(self):
232 # If the suite is new and its release directory does not exist
233@@ -3095,7 +3121,7 @@
234 suite = series.name + pocketsuffix[spph.pocket]
235 release_data = self.makeFakeReleaseData()
236 release_path = os.path.join(
237- self.getReleaseFileDir(root, series, suite), "Release")
238+ self.getReleaseFileDir(root, series, suite), "Release.new")
239
240 self.makePublisher(series)._writeReleaseFile(suite, release_data)
241