8687.15.12
by Karl Fogel
Add the copyright header block to files under lib/lp/archivepublisher/ |
1 |
# Copyright 2009 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
3 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
4 |
__all__ = ['DiskPoolEntry', 'DiskPool', 'poolify', 'unpoolify'] |
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
5 |
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
6 |
import os |
3023.3.60
by Daniel Silverstone
Ensure that files are published into the pool properly, or not at all |
7 |
import tempfile |
3496.1.30
by Celso Providelo
partial review comments from kiko applied. |
8 |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
9 |
from canonical.librarian.utils import ( |
10 |
copy_and_close, |
|
11 |
sha1_from_path, |
|
12 |
)
|
|
8426.7.1
by Julian Edwards
migrate archivepublisher to the lp tree. |
13 |
from lp.archivepublisher import HARDCODED_COMPONENT_ORDER |
11382.6.34
by Gavin Panella
Reformat imports in all files touched so far. |
14 |
from lp.services.propertycache import cachedproperty |
7675.332.1
by Celso Providelo
Dealing with MissingSymlinkInPool situation in DeathRow. |
15 |
from lp.soyuz.interfaces.publishing import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
16 |
MissingSymlinkInPool, |
17 |
NotInPool, |
|
18 |
PoolFileOverwriteError, |
|
19 |
)
|
|
3500.2.38
by Celso Providelo
Checkpoint version, don't use it tests are broken, will need more time to fix 'publishing from content class' and don't want to loose this changeset |
20 |
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
21 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
22 |
def poolify(source, component): |
23 |
"""Poolify a given source and component name."""
|
|
24 |
if source.startswith("lib"): |
|
25 |
return os.path.join(component, source[:4], source) |
|
26 |
else: |
|
27 |
return os.path.join(component, source[:1], source) |
|
28 |
||
29 |
||
30 |
def unpoolify(self, path): |
|
31 |
"""Take a path and unpoolify it.
|
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
32 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
33 |
Return a tuple of component, source, filename
|
1102
by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs |
34 |
"""
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
35 |
p = path.split("/") |
36 |
if len(p) < 3 or len(p) > 4: |
|
37 |
raise ValueError("Path %s is not in a valid pool form" % path) |
|
38 |
if len(p) == 4: |
|
39 |
return p[0], p[2], p[3] |
|
40 |
return p[0], p[2], None |
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
41 |
|
2890.2.4
by Daniel Silverstone
Make sure symlinks are relative and sanitised. Also ensure we split on tabs only on extra override input and split the distrorelease name slightly more carefully. |
42 |
|
43 |
def relative_symlink(src_path, dst_path): |
|
44 |
"""os.symlink replacement that creates relative symbolic links."""
|
|
45 |
path_sep = os.path.sep |
|
46 |
src_path = os.path.normpath(src_path) |
|
47 |
dst_path = os.path.normpath(dst_path) |
|
48 |
src_path_elems = src_path.split(path_sep) |
|
49 |
dst_path_elems = dst_path.split(path_sep) |
|
50 |
if os.path.isabs(src_path): |
|
51 |
if not os.path.isabs(dst_path): |
|
52 |
dst_path = os.path.abspath(dst_path) |
|
53 |
common_prefix = os.path.commonprefix([src_path_elems, dst_path_elems]) |
|
54 |
backward_elems = ['..'] * (len(dst_path_elems)-len(common_prefix)-1) |
|
55 |
forward_elems = src_path_elems[len(common_prefix):] |
|
56 |
src_path = path_sep.join(backward_elems + forward_elems) |
|
57 |
os.symlink(src_path, dst_path) |
|
58 |
||
3691.212.1
by Malcolm Cleaton
Checkpoint |
59 |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
60 |
class FileAddActionEnum: |
61 |
"""Possible actions taken when adding a file.
|
|
62 |
||
63 |
FILE_ADDED: we added the actual file to the disk
|
|
64 |
SYMLINK_ADDED: we created a symlink to another copy of the same file
|
|
65 |
NONE: no action was necessary or taken.
|
|
66 |
"""
|
|
67 |
FILE_ADDED = "file_added" |
|
68 |
SYMLINK_ADDED = "symlink_added" |
|
69 |
NONE = "none" |
|
70 |
||
71 |
||
3023.3.60
by Daniel Silverstone
Ensure that files are published into the pool properly, or not at all |
72 |
class _diskpool_atomicfile: |
73 |
"""Simple file-like object used by the pool to atomically move into place
|
|
74 |
a file after downloading from the librarian.
|
|
75 |
||
76 |
This class is designed to solve a very specific problem encountered in
|
|
77 |
the publisher. Namely that should the publisher crash during the process
|
|
78 |
of publishing a file to the pool, an empty or incomplete file would be
|
|
79 |
present in the pool. Its mere presence would fool the publisher into
|
|
80 |
believing it had already downloaded that file to the pool, resulting
|
|
81 |
in failures in the apt-ftparchive stage.
|
|
82 |
||
83 |
By performing a rename() when the file is guaranteed to have been
|
|
84 |
fully written to disk (after the fd.close()) we can be sure that if
|
|
85 |
the filename is present in the pool, it is definitely complete.
|
|
86 |
"""
|
|
87 |
||
88 |
def __init__(self, targetfilename, mode, rootpath="/tmp"): |
|
3496.1.45
by Celso Providelo
Postponed review issues and fixing pylint errors |
89 |
# atomicfile implements the file object interface, but it is only
|
90 |
# really used (or useful) for writing binary files, which is why we
|
|
91 |
# keep the mode constructor argument but assert it's sane below.
|
|
3023.3.60
by Daniel Silverstone
Ensure that files are published into the pool properly, or not at all |
92 |
if mode == "w": |
93 |
mode = "wb" |
|
94 |
assert mode == "wb" |
|
3496.1.45
by Celso Providelo
Postponed review issues and fixing pylint errors |
95 |
|
3496.1.44
by Celso Providelo
review comments from kiko (take I and II), missing publishing/pool redesign and tests |
96 |
assert not os.path.exists(targetfilename) |
97 |
||
3023.3.60
by Daniel Silverstone
Ensure that files are published into the pool properly, or not at all |
98 |
self.targetfilename = targetfilename |
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
99 |
fd, name = tempfile.mkstemp(prefix="temp-download.", dir=rootpath) |
3023.3.60
by Daniel Silverstone
Ensure that files are published into the pool properly, or not at all |
100 |
self.fd = os.fdopen(fd, mode) |
101 |
self.tempname = name |
|
102 |
self.write = self.fd.write |
|
103 |
||
104 |
def close(self): |
|
105 |
"""Make the atomic move into place having closed the temp file."""
|
|
106 |
self.fd.close() |
|
3023.3.86
by Daniel Silverstone
Fix up permissions in pool files |
107 |
os.chmod(self.tempname, 0644) |
3147.4.1
by Daniel Silverstone
Review response |
108 |
# Note that this will fail if the target and the temp dirs are on
|
109 |
# different filesystems.
|
|
3023.3.60
by Daniel Silverstone
Ensure that files are published into the pool properly, or not at all |
110 |
os.rename(self.tempname, self.targetfilename) |
111 |
||
2890.2.4
by Daniel Silverstone
Make sure symlinks are relative and sanitised. Also ensure we split on tabs only on extra override input and split the distrorelease name slightly more carefully. |
112 |
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
113 |
class DiskPoolEntry: |
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
114 |
"""Represents a single file in the pool, across all components.
|
115 |
||
116 |
Creating a DiskPoolEntry performs disk reads, so don't create an
|
|
117 |
instance of this class unless you need to know what's already on
|
|
118 |
the disk for this file.
|
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
119 |
|
4162.4.3
by Celso Providelo
applying review comments [r=statik] |
120 |
'tempath' must be in the same filesystem as 'rootpath', it will be
|
121 |
used to store the instalation candidate while it is being downloaded
|
|
122 |
from the Librarian.
|
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
123 |
|
4162.4.3
by Celso Providelo
applying review comments [r=statik] |
124 |
Remaining files in the 'temppath' indicated installation failures and
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
125 |
require manual removal after further investigation.
|
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
126 |
"""
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
127 |
def __init__(self, rootpath, temppath, source, filename, logger): |
3691.212.1
by Malcolm Cleaton
Checkpoint |
128 |
self.rootpath = rootpath |
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
129 |
self.temppath = temppath |
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
130 |
self.source = source |
3691.212.1
by Malcolm Cleaton
Checkpoint |
131 |
self.filename = filename |
132 |
self.logger = logger |
|
133 |
||
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
134 |
self.file_component = None |
3691.212.1
by Malcolm Cleaton
Checkpoint |
135 |
self.symlink_components = set() |
2890.2.4
by Daniel Silverstone
Make sure symlinks are relative and sanitised. Also ensure we split on tabs only on extra override input and split the distrorelease name slightly more carefully. |
136 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
137 |
for component in HARDCODED_COMPONENT_ORDER: |
138 |
path = self.pathFor(component) |
|
139 |
if os.path.islink(path): |
|
140 |
self.symlink_components.add(component) |
|
141 |
elif os.path.isfile(path): |
|
142 |
assert not self.file_component |
|
143 |
self.file_component = component |
|
144 |
if self.symlink_components: |
|
145 |
assert self.file_component |
|
146 |
||
3691.212.2
by Malcolm Cleaton
More refactoring |
147 |
def debug(self, *args, **kwargs): |
148 |
self.logger.debug(*args, **kwargs) |
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
149 |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
150 |
def pathFor(self, component): |
151 |
"""Return the path for this file in the given component."""
|
|
152 |
return os.path.join(self.rootpath, |
|
153 |
poolify(self.source, component), |
|
154 |
self.filename) |
|
155 |
||
156 |
def preferredComponent(self, add=None, remove=None): |
|
157 |
"""Return the appropriate component for the real file.
|
|
158 |
||
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
159 |
If add is passed, add it to the list before calculating.
|
160 |
If remove is passed, remove it before calculating.
|
|
3691.212.2
by Malcolm Cleaton
More refactoring |
161 |
Thus, we can calcuate which component should contain the main file
|
162 |
after the addition or removal we are working on.
|
|
3496.1.28
by Celso Providelo
Fix publishing core, add unit tests for several publishing cases, check potetial archive corruption (pair-programming with malcc) |
163 |
"""
|
3691.212.2
by Malcolm Cleaton
More refactoring |
164 |
components = set() |
165 |
if self.file_component: |
|
166 |
components.add(self.file_component) |
|
167 |
components = components.union(self.symlink_components) |
|
168 |
if add is not None: |
|
169 |
components.add(add) |
|
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
170 |
if remove is not None and remove in components: |
3691.212.2
by Malcolm Cleaton
More refactoring |
171 |
components.remove(remove) |
172 |
||
173 |
for component in HARDCODED_COMPONENT_ORDER: |
|
174 |
if component in components: |
|
175 |
return component |
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
176 |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
177 |
@cachedproperty
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
178 |
def file_hash(self): |
3691.212.2
by Malcolm Cleaton
More refactoring |
179 |
"""Return the SHA1 sum of this file."""
|
180 |
targetpath = self.pathFor(self.file_component) |
|
181 |
return sha1_from_path(targetpath) |
|
182 |
||
183 |
def addFile(self, component, sha1, contents): |
|
184 |
"""See DiskPool.addFile."""
|
|
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
185 |
assert component in HARDCODED_COMPONENT_ORDER |
186 |
||
3691.212.2
by Malcolm Cleaton
More refactoring |
187 |
targetpath = self.pathFor(component) |
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
188 |
if not os.path.exists(os.path.dirname(targetpath)): |
189 |
os.makedirs(os.path.dirname(targetpath)) |
|
3496.1.44
by Celso Providelo
review comments from kiko (take I and II), missing publishing/pool redesign and tests |
190 |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
191 |
if self.file_component: |
192 |
# There's something on disk. Check hash.
|
|
193 |
if sha1 != self.file_hash: |
|
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
194 |
raise PoolFileOverwriteError('%s != %s for %s' % |
7726.2.1
by Celso Providelo
Fixing bug #324274 (misleading deathrow optimizations of file checksums). Also re-write doc/deathrow.txt to be more readable and sampledata-independent. |
195 |
(sha1, self.file_hash, |
196 |
self.pathFor(self.file_component))) |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
197 |
|
198 |
if (component == self.file_component |
|
199 |
or component in self.symlink_components): |
|
200 |
# The file is already here
|
|
201 |
return FileAddActionEnum.NONE |
|
202 |
else: |
|
7726.2.1
by Celso Providelo
Fixing bug #324274 (misleading deathrow optimizations of file checksums). Also re-write doc/deathrow.txt to be more readable and sampledata-independent. |
203 |
# The file is present in a different component,
|
204 |
# make a symlink.
|
|
205 |
relative_symlink( |
|
206 |
self.pathFor(self.file_component), targetpath) |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
207 |
self.symlink_components.add(component) |
208 |
# Then fix to ensure the right component is linked.
|
|
209 |
self._sanitiseLinks() |
|
210 |
||
211 |
return FileAddActionEnum.SYMLINK_ADDED |
|
212 |
||
213 |
# If we get to here, we want to write the file.
|
|
3691.212.6
by Malcolm Cleaton
Addressed review comments. |
214 |
assert not os.path.exists(targetpath) |
215 |
||
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
216 |
self.debug("Making new file in %s for %s/%s" % |
3691.212.1
by Malcolm Cleaton
Checkpoint |
217 |
(component, self.source, self.filename)) |
3496.1.28
by Celso Providelo
Fix publishing core, add unit tests for several publishing cases, check potetial archive corruption (pair-programming with malcc) |
218 |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
219 |
file_to_write = _diskpool_atomicfile( |
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
220 |
targetpath, "wb", rootpath=self.temppath) |
3691.212.2
by Malcolm Cleaton
More refactoring |
221 |
contents.open() |
222 |
copy_and_close(contents, file_to_write) |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
223 |
self.file_component = component |
3691.212.2
by Malcolm Cleaton
More refactoring |
224 |
return FileAddActionEnum.FILE_ADDED |
3496.1.28
by Celso Providelo
Fix publishing core, add unit tests for several publishing cases, check potetial archive corruption (pair-programming with malcc) |
225 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
226 |
def removeFile(self, component): |
3691.93.1
by Christian Reis
Factor the death row processing into a separate script. Implement the DeathRow processor, with size calculation, some optimizations and properly restricting it to a specific distribution, and a process-death-row script, which has a dry run mode. Do minor cleanups in DAR.publish(), implementing a DR.isUnstable() method. Test DeathRow. Change DiskPool file removal code to return the filesize of the file removed. Implement Source/BinaryPackageFilePublishing.displayname, with tests. Clean up publish-distro as much as I can.. |
227 |
"""Remove a file from a given component; return bytes freed.
|
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
228 |
|
229 |
This method handles three situations:
|
|
230 |
||
3691.212.1
by Malcolm Cleaton
Checkpoint |
231 |
1) Remove a symlink
|
232 |
||
233 |
2) Remove the main file and there are no symlinks left.
|
|
234 |
||
235 |
3) Remove the main file and there are symlinks left.
|
|
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
236 |
"""
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
237 |
if not self.file_component: |
7675.332.1
by Celso Providelo
Dealing with MissingSymlinkInPool situation in DeathRow. |
238 |
raise NotInPool( |
239 |
"File for removing %s %s/%s is not in pool, skipping." % |
|
240 |
(component, self.source, self.filename)) |
|
241 |
||
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
242 |
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
243 |
# Okay, it's there, if it's a symlink then we need to remove
|
244 |
# it simply.
|
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
245 |
if component in self.symlink_components: |
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
246 |
self.debug("Removing %s %s/%s as it is a symlink" |
3691.212.1
by Malcolm Cleaton
Checkpoint |
247 |
% (component, self.source, self.filename)) |
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
248 |
# ensure we are removing a symbolic link and
|
249 |
# it is published in one or more components
|
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
250 |
link_path = self.pathFor(component) |
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
251 |
assert os.path.islink(link_path) |
3691.212.1
by Malcolm Cleaton
Checkpoint |
252 |
return self._reallyRemove(component) |
253 |
||
7675.332.1
by Celso Providelo
Dealing with MissingSymlinkInPool situation in DeathRow. |
254 |
if component != self.file_component: |
255 |
raise MissingSymlinkInPool( |
|
256 |
"Symlink for %s/%s in %s is missing, skipping." % |
|
257 |
(self.source, self.filename, component)) |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
258 |
|
259 |
# It's not a symlink, this means we need to check whether we
|
|
260 |
# have symlinks or not.
|
|
261 |
if len(self.symlink_components) == 0: |
|
7726.2.1
by Celso Providelo
Fixing bug #324274 (misleading deathrow optimizations of file checksums). Also re-write doc/deathrow.txt to be more readable and sampledata-independent. |
262 |
self.debug("Removing %s/%s from %s" % |
3691.212.1
by Malcolm Cleaton
Checkpoint |
263 |
(self.source, self.filename, component)) |
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
264 |
else: |
3691.92.1
by Malcolm Cleaton
Fix bug 57237 |
265 |
# The target for removal is the real file, and there are symlinks
|
266 |
# pointing to it. In order to avoid breakage, we need to first
|
|
267 |
# shuffle the symlinks, so that the one we want to delete will
|
|
3691.212.2
by Malcolm Cleaton
More refactoring |
268 |
# just be one of the links, and becomes safe.
|
269 |
targetcomponent = self.preferredComponent(remove=component) |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
270 |
self._shufflesymlinks(targetcomponent) |
271 |
||
272 |
return self._reallyRemove(component) |
|
273 |
||
274 |
def _reallyRemove(self, component): |
|
3691.93.1
by Christian Reis
Factor the death row processing into a separate script. Implement the DeathRow processor, with size calculation, some optimizations and properly restricting it to a specific distribution, and a process-death-row script, which has a dry run mode. Do minor cleanups in DAR.publish(), implementing a DR.isUnstable() method. Test DeathRow. Change DiskPool file removal code to return the filesize of the file removed. Implement Source/BinaryPackageFilePublishing.displayname, with tests. Clean up publish-distro as much as I can.. |
275 |
"""Remove file and return file size.
|
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
276 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
277 |
Remove the file from the filesystem and from our data
|
3691.93.1
by Christian Reis
Factor the death row processing into a separate script. Implement the DeathRow processor, with size calculation, some optimizations and properly restricting it to a specific distribution, and a process-death-row script, which has a dry run mode. Do minor cleanups in DAR.publish(), implementing a DR.isUnstable() method. Test DeathRow. Change DiskPool file removal code to return the filesize of the file removed. Implement Source/BinaryPackageFilePublishing.displayname, with tests. Clean up publish-distro as much as I can.. |
278 |
structures.
|
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
279 |
"""
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
280 |
fullpath = self.pathFor(component) |
3691.93.1
by Christian Reis
Factor the death row processing into a separate script. Implement the DeathRow processor, with size calculation, some optimizations and properly restricting it to a specific distribution, and a process-death-row script, which has a dry run mode. Do minor cleanups in DAR.publish(), implementing a DR.isUnstable() method. Test DeathRow. Change DiskPool file removal code to return the filesize of the file removed. Implement Source/BinaryPackageFilePublishing.displayname, with tests. Clean up publish-distro as much as I can.. |
281 |
assert os.path.exists(fullpath) |
282 |
||
3691.212.1
by Malcolm Cleaton
Checkpoint |
283 |
if component == self.file_component: |
284 |
# Deleting the master file is only allowed if there
|
|
285 |
# are no symlinks left.
|
|
286 |
assert not self.symlink_components |
|
287 |
self.file_component = None |
|
288 |
elif component in self.symlink_components: |
|
289 |
self.symlink_components.remove(component) |
|
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
290 |
|
3691.93.1
by Christian Reis
Factor the death row processing into a separate script. Implement the DeathRow processor, with size calculation, some optimizations and properly restricting it to a specific distribution, and a process-death-row script, which has a dry run mode. Do minor cleanups in DAR.publish(), implementing a DR.isUnstable() method. Test DeathRow. Change DiskPool file removal code to return the filesize of the file removed. Implement Source/BinaryPackageFilePublishing.displayname, with tests. Clean up publish-distro as much as I can.. |
291 |
size = os.lstat(fullpath).st_size |
292 |
os.remove(fullpath) |
|
293 |
return size |
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
294 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
295 |
def _shufflesymlinks(self, targetcomponent): |
3496.1.30
by Celso Providelo
partial review comments from kiko applied. |
296 |
"""Shuffle the symlinks for filename so that targetcomponent contains
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
297 |
the real file and the rest are symlinks to the right place..."""
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
298 |
if targetcomponent == self.file_component: |
3691.87.2
by Malcolm Cleaton
Tidying up |
299 |
# We're already in the right place.
|
3691.87.1
by Malcolm Cleaton
Fix and re-enable diskpool sanitiseLinks (bug 55896) |
300 |
return
|
3496.1.45
by Celso Providelo
Postponed review issues and fixing pylint errors |
301 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
302 |
if targetcomponent not in self.symlink_components: |
3496.1.46
by Celso Providelo
Finishing poll review take II (sanitizeLinks dead) |
303 |
raise ValueError( |
3691.212.1
by Malcolm Cleaton
Checkpoint |
304 |
"Target component '%s' is not a symlink for %s" % |
305 |
(targetcomponent, self.filename)) |
|
3496.1.45
by Celso Providelo
Postponed review issues and fixing pylint errors |
306 |
|
3691.87.1
by Malcolm Cleaton
Fix and re-enable diskpool sanitiseLinks (bug 55896) |
307 |
self.debug("Shuffling symlinks so primary for %s is in %s" % |
3691.212.1
by Malcolm Cleaton
Checkpoint |
308 |
(self.filename, targetcomponent)) |
3691.87.1
by Malcolm Cleaton
Fix and re-enable diskpool sanitiseLinks (bug 55896) |
309 |
|
3691.87.2
by Malcolm Cleaton
Tidying up |
310 |
# Okay, so first up, we unlink the targetcomponent symlink.
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
311 |
targetpath = self.pathFor(targetcomponent) |
3691.87.1
by Malcolm Cleaton
Fix and re-enable diskpool sanitiseLinks (bug 55896) |
312 |
os.remove(targetpath) |
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
313 |
|
3691.87.2
by Malcolm Cleaton
Tidying up |
314 |
# Now we rename the source file into the target component.
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
315 |
sourcepath = self.pathFor(self.file_component) |
3496.1.44
by Celso Providelo
review comments from kiko (take I and II), missing publishing/pool redesign and tests |
316 |
|
4664.1.1
by Curtis Hovey
Normalized comments for bug 3732. |
317 |
# XXX cprov 2006-05-26: if it fails the symlinks are severely broken
|
3496.1.33
by Celso Providelo
Trace an issue in archive_removal related to corrupted symlinks. |
318 |
# or maybe we are writing them wrong. It needs manual fix !
|
319 |
# Nonetheless, we carry on checking other candidates.
|
|
320 |
# Use 'find -L . -type l' on pool to find out broken symlinks
|
|
321 |
# Normally they only can be fixed by remove the broken links and
|
|
322 |
# run a careful (-C) publication.
|
|
3496.1.45
by Celso Providelo
Postponed review issues and fixing pylint errors |
323 |
|
324 |
# ensure targetpath doesn't exists and the sourcepath exists
|
|
325 |
# before rename them.
|
|
326 |
assert not os.path.exists(targetpath) |
|
327 |
assert os.path.exists(sourcepath) |
|
328 |
os.rename(sourcepath, targetpath) |
|
329 |
||
4664.1.1
by Curtis Hovey
Normalized comments for bug 3732. |
330 |
# XXX cprov 2006-06-12: it may cause problems to the database, since
|
3691.113.1
by Malcolm Cleaton
Tidying XXX comments in archivepublisher |
331 |
# ZTM isn't handled properly in scripts/publish-distro.py. Things are
|
3496.1.45
by Celso Providelo
Postponed review issues and fixing pylint errors |
332 |
# commited mid-procedure & bare exception is caught.
|
333 |
||
3691.212.1
by Malcolm Cleaton
Checkpoint |
334 |
# Update the data structures.
|
335 |
self.symlink_components.add(self.file_component) |
|
336 |
self.symlink_components.remove(targetcomponent) |
|
337 |
self.file_component = targetcomponent |
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
338 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
339 |
# Now we make the symlinks on the filesystem.
|
340 |
for comp in self.symlink_components: |
|
341 |
newpath = self.pathFor(comp) |
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
342 |
try: |
343 |
os.remove(newpath) |
|
344 |
except OSError: |
|
3691.87.2
by Malcolm Cleaton
Tidying up |
345 |
# Do nothing because it's almost certainly a not found.
|
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
346 |
pass
|
2890.2.4
by Daniel Silverstone
Make sure symlinks are relative and sanitised. Also ensure we split on tabs only on extra override input and split the distrorelease name slightly more carefully. |
347 |
relative_symlink(targetpath, newpath) |
1932
by Canonical.com Patch Queue Manager
Merge in publishing work from soyuz sprint. r=jamesh |
348 |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
349 |
def _sanitiseLinks(self): |
3691.212.1
by Malcolm Cleaton
Checkpoint |
350 |
"""Ensure the real file is in the most preferred component.
|
351 |
||
352 |
If this file is in more than one component, ensure the real
|
|
353 |
file is in the most preferred component and the other components
|
|
354 |
use symlinks.
|
|
355 |
||
356 |
It's important that the real file be in the most preferred
|
|
357 |
component because partial mirrors may only take a subset of
|
|
3691.87.1
by Malcolm Cleaton
Fix and re-enable diskpool sanitiseLinks (bug 55896) |
358 |
components, and these partial mirrors must not have broken
|
359 |
symlinks where they should have working files.
|
|
360 |
"""
|
|
3691.212.2
by Malcolm Cleaton
More refactoring |
361 |
component = self.preferredComponent() |
362 |
if not self.file_component == component: |
|
363 |
self._shufflesymlinks(component) |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
364 |
|
365 |
||
366 |
class DiskPool: |
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
367 |
"""Scan a pool on the filesystem and record information about it.
|
368 |
||
4162.4.3
by Celso Providelo
applying review comments [r=statik] |
369 |
Its constructor receives 'rootpath', which is the pool path where the
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
370 |
files will be installed, and the 'temppath', which is a temporary
|
4162.4.3
by Celso Providelo
applying review comments [r=statik] |
371 |
directory used to store the installation candidate from librarian.
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
372 |
|
373 |
'rootpath' and 'temppath' must be in the same filesystem, see
|
|
374 |
DiskPoolEntry for further information.
|
|
375 |
"""
|
|
3691.212.2
by Malcolm Cleaton
More refactoring |
376 |
results = FileAddActionEnum |
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
377 |
|
378 |
def __init__(self, rootpath, temppath, logger): |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
379 |
self.rootpath = rootpath |
380 |
if not rootpath.endswith("/"): |
|
381 |
self.rootpath += "/" |
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
382 |
|
383 |
self.temppath = temppath |
|
4162.4.3
by Celso Providelo
applying review comments [r=statik] |
384 |
if not temppath.endswith("/"): |
385 |
self.temppath += "/" |
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
386 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
387 |
self.entries = {} |
388 |
self.logger = logger |
|
389 |
||
3691.212.2
by Malcolm Cleaton
More refactoring |
390 |
def _getEntry(self, sourcename, file): |
391 |
"""Return a new DiskPoolEntry for the given sourcename and file."""
|
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
392 |
return DiskPoolEntry( |
393 |
self.rootpath, self.temppath, sourcename, file, self.logger) |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
394 |
|
395 |
def pathFor(self, comp, source, file=None): |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
396 |
"""Return the path for the given pool folder or file.
|
397 |
||
398 |
If file is none, the path to the folder containing all packages
|
|
399 |
for the given component and source package name will be returned.
|
|
400 |
||
401 |
If file is specified, the path to the specific package file will
|
|
402 |
be returned.
|
|
403 |
"""
|
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
404 |
path = os.path.join( |
405 |
self.rootpath, poolify(source, comp)) |
|
406 |
if file: |
|
407 |
return os.path.join(path, file) |
|
408 |
return path |
|
409 |
||
3691.212.2
by Malcolm Cleaton
More refactoring |
410 |
def addFile(self, component, sourcename, filename, sha1, contents): |
411 |
"""Add a file with the given contents to the pool.
|
|
412 |
||
413 |
Component, sourcename and filename are used to calculate the
|
|
414 |
on-disk location.
|
|
415 |
||
416 |
sha1 is used to compare with the existing file's checksum, if
|
|
417 |
a file already exists for any component.
|
|
418 |
||
419 |
contents is a file-like object containing the contents we want
|
|
420 |
to write.
|
|
421 |
||
422 |
There are four possible outcomes:
|
|
423 |
- If the file doesn't exist in the pool for any component, it will
|
|
424 |
be written from the given contents and results.ADDED_FILE will be
|
|
425 |
returned.
|
|
426 |
||
427 |
- If the file already exists in the pool, in this or any other
|
|
428 |
component, the checksum of the file on disk will be calculated and
|
|
429 |
compared with the checksum provided. If they fail to match,
|
|
430 |
PoolFileOverwriteError will be raised.
|
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
431 |
|
3691.212.2
by Malcolm Cleaton
More refactoring |
432 |
- If the file already exists but not in this component, and the
|
433 |
checksum test above passes, a symlink will be added, and
|
|
434 |
results.SYMLINK_ADDED will be returned. Also, the symlinks will be
|
|
435 |
checked and sanitised, to ensure the real copy of the file is in the
|
|
436 |
most preferred component, according to HARDCODED_COMPONENT_ORDER.
|
|
437 |
||
438 |
- If the file already exists and is already in this component,
|
|
439 |
either as a file or a symlink, and the checksum check passes,
|
|
440 |
results.NONE will be returned and nothing will be done.
|
|
441 |
"""
|
|
442 |
entry = self._getEntry(sourcename, filename) |
|
443 |
return entry.addFile(component, sha1, contents) |
|
4162.4.1
by Celso Providelo
Fix bug #110351 (lost doffiles in archive breaks dsync/mirroring). Adding a temporary directory in the archive-tree for storing files while they are downloaded from librarian. |
444 |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
445 |
def removeFile(self, component, sourcename, filename): |
3691.212.2
by Malcolm Cleaton
More refactoring |
446 |
"""Remove the specified file from the pool.
|
447 |
||
448 |
There are three possible outcomes:
|
|
449 |
- If the specified file does not exist, NotInPool will be raised.
|
|
450 |
||
451 |
- If the specified file exists and is a symlink, or is the only
|
|
452 |
copy of the file in the pool, it will simply be deleted, and its
|
|
453 |
size will be returned.
|
|
454 |
||
455 |
- If the specified file is a real file and there are symlinks
|
|
456 |
referencing it, the symlink in the next most preferred component
|
|
457 |
will be deleted, and the file will be moved to replace it. The
|
|
458 |
size of the deleted symlink will be returned.
|
|
459 |
"""
|
|
460 |
entry = self._getEntry(sourcename, filename) |
|
3691.212.1
by Malcolm Cleaton
Checkpoint |
461 |
return entry.removeFile(component) |