~launchpad-pqm/launchpad/devel

3673.6.16 by Malcolm Cleaton
Addressed review comments
1
# Copyright 2006 Canonical Ltd.  All rights reserved.
2
#
3
4
"""Code for 'processing' 'uploads'. Also see nascentupload.py.
5
6
Uploads are directories in the 'incoming' queue directory. They may have
7
arrived manually from a distribution contributor, via a poppy upload, or
8
they may have come from a build.
9
10
Within an upload, we may find no changes file, one, or several. One is
11
the usual number. To process the upload, we process each changes file
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
12
in turn. These changes files may be within a structure of sub-directories,
13
in which case we extract information from the names of these, to calculate
14
which distribution and which PPA are being uploaded to.
3673.6.16 by Malcolm Cleaton
Addressed review comments
15
16
To process a changes file, we make checks such as that the other files
17
referenced by it are present, formatting is valid, signatures are correct,
18
checksums match, and that the .changes file represents an upload which makes
19
sense, eg. it is not a binary for which we have no source, or an older
4285.2.8 by Mark Shuttleworth
Test fixes for archive uploader
20
version than already exists in the same target distroseries pocket.
3673.6.16 by Malcolm Cleaton
Addressed review comments
21
22
Depending on the outcome of these checks, the changes file will either be
23
accepted (and the information from it, and the referenced files, imported
24
into the database) or it won't (and the database will be unchanged). If not
25
accepted, a changes file might be 'failed' or 'rejected', where failed
26
changes files are dropped silently, but rejected ones generate a rejection
27
email back to the uploader.
28
29
There are several valid reasons to fail (the changes file is so mangled
30
that we can't read who we should send a rejection to, or it's not correctly
31
signed, so we can't be sure a rejection wouldn't be spam (it may not have
32
been uploaded by who it says it was uploaded by). In practice, in the code
33
as it stands, we also consider the processing of a changes file to have
34
failed if it generates an unexpected exception, and there are some known
35
cases where it does this and a rejection would have been more useful
36
(see bug 35965).
37
38
Each upload directory is saved after processing, in case it is needed for
39
debugging purposes. This is done by moving it to a directory inside the queue
40
directory, beside incoming, named after the result - 'failed', 'rejected' or
41
'accepted'. Where there are no changes files, the upload is considered failed,
42
and where there is more than one changes file, the upload is assigned the
43
worst of the results from the various changes files found (in the order
44
above, failed being worst).
45
46
"""
47
48
__metaclass__ = type
49
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
50
import os
3804.1.12 by Celso Providelo
Fixing soyuz-set-of-upload and related code.
51
import shutil
5089.1.2 by Celso Providelo
applying review comments, r=kiko.
52
import stat
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
53
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
54
from zope.component import getUtility
55
4197.1.1 by Christian Reis
Break apart upload code into canonical/archiveuploader
56
from canonical.archiveuploader.nascentupload import (
4936.3.4 by Julian Edwards
Improve the API for error checking and early rejection.
57
    NascentUpload, FatalUploadError, EarlyReturnUploadError)
4197.1.1 by Christian Reis
Break apart upload code into canonical/archiveuploader
58
from canonical.archiveuploader.uploadpolicy import (
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
59
    findPolicyByOptions, UploadPolicyError)
3691.443.65 by Celso Providelo
Automatically creating PPAs based on the first successfully upload. Adding Archive.description text.
60
from canonical.launchpad.interfaces import (
5028.1.2 by David Murphy
Removed ArchivePurpose from canonical.lp.dbschema.
61
    ArchivePurpose, IDistributionSet, IPersonSet, NotFoundError)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
62
63
from contrib.glock import GlobalLock
64
65
__all__ = ['UploadProcessor']
66
3673.6.20 by Malcolm Cleaton
Second review comments
67
68
class UploadStatusEnum:
69
    """Possible results from processing an upload.
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
70
3673.6.20 by Malcolm Cleaton
Second review comments
71
    ACCEPTED: all goes well, we commit nascentupload's changes to the db
72
    REJECTED: nascentupload gives a well-formed rejection error,
73
              we send a rejection email and rollback.
74
    FAILED: nascentupload code raises an exception, no email, rollback
75
    """
76
    ACCEPTED = 'accepted'
77
    REJECTED = 'rejected'
78
    FAILED = 'failed'
79
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
80
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
81
class UploadPathError(Exception):
82
    """This exception happened when parsing the upload path."""
83
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
84
class PPAUploadPathError(Exception):
85
    """Exception when parsing a PPA upload path."""
86
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
87
class UploadProcessor:
3673.6.16 by Malcolm Cleaton
Addressed review comments
88
    """Responsible for processing uploads. See module docstring."""
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
89
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
90
    def __init__(self, options, ztm, log):
91
        self.options = options
92
        self.ztm = ztm
93
        self.log = log
4162.3.2 by Celso Providelo
correct varname.
94
        self.last_processed_upload = None
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
95
3673.6.20 by Malcolm Cleaton
Second review comments
96
    def processUploadQueue(self):
3673.6.16 by Malcolm Cleaton
Addressed review comments
97
        """Search for uploads, and process them.
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
98
3673.6.16 by Malcolm Cleaton
Addressed review comments
99
	Uploads are searched for in the 'incoming' directory inside the
100
        base_fsroot.
101
102
        This method also creates the 'incoming', 'accepted', 'rejected', and
103
        'failed' directories inside the base_fsroot if they don't yet exist.
104
        """
3673.6.7 by Malcolm Cleaton
Incorporate peer-review suggestions from dsilvers
105
        try:
106
            self.log.debug("Beginning processing")
3673.6.16 by Malcolm Cleaton
Addressed review comments
107
3673.6.7 by Malcolm Cleaton
Incorporate peer-review suggestions from dsilvers
108
            for subdir in ["incoming", "accepted", "rejected", "failed"]:
109
                full_subdir = os.path.join(self.options.base_fsroot, subdir)
110
                if not os.path.exists(full_subdir):
111
                    self.log.debug("Creating directory %s" % full_subdir)
112
                    os.mkdir(full_subdir)
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
113
3673.6.7 by Malcolm Cleaton
Incorporate peer-review suggestions from dsilvers
114
            fsroot = os.path.join(self.options.base_fsroot, "incoming")
3673.6.16 by Malcolm Cleaton
Addressed review comments
115
            uploads_to_process = self.locateDirectories(fsroot)
3673.6.7 by Malcolm Cleaton
Incorporate peer-review suggestions from dsilvers
116
            self.log.debug("Checked in %s, found %s"
3673.6.16 by Malcolm Cleaton
Addressed review comments
117
                           % (fsroot, uploads_to_process))
118
            for upload in uploads_to_process:
119
                self.log.debug("Considering upload %s" % upload)
120
                self.processUpload(fsroot, upload)
3673.6.7 by Malcolm Cleaton
Incorporate peer-review suggestions from dsilvers
121
122
        finally:
123
            self.log.debug("Rolling back any remaining transactions.")
124
            self.ztm.abort()
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
125
3673.6.16 by Malcolm Cleaton
Addressed review comments
126
    def processUpload(self, fsroot, upload):
127
        """Process an upload's changes files, and move it to a new directory.
128
129
        The destination directory depends on the result of the processing
130
        of the changes files. If there are no changes files, the result
131
        is 'failed', otherwise it is the worst of the results from the
132
        individual changes files, in order 'failed', 'rejected', 'accepted'.
133
134
        If the leafname option is set but its value is not the same as the
135
        name of the upload directory, skip it entirely.
136
137
        """
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
138
        if (self.options.leafname is not None and
3673.6.16 by Malcolm Cleaton
Addressed review comments
139
            upload != self.options.leafname):
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
140
            self.log.debug("Skipping %s -- does not match %s" % (
3673.6.16 by Malcolm Cleaton
Addressed review comments
141
                upload, self.options.leafname))
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
142
            return
143
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
144
        upload_path = os.path.join(fsroot, upload)
3673.6.16 by Malcolm Cleaton
Addressed review comments
145
        changes_files = self.locateChangesFiles(upload_path)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
146
147
        # Keep track of the various results
148
        some_failed = False
149
        some_rejected = False
150
        some_accepted = False
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
151
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
152
        for changes_file in changes_files:
153
            self.log.debug("Considering changefile %s" % changes_file)
154
            try:
3673.6.16 by Malcolm Cleaton
Addressed review comments
155
                result = self.processChangesFile(upload_path, changes_file)
3673.6.20 by Malcolm Cleaton
Second review comments
156
                if result == UploadStatusEnum.FAILED:
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
157
                    some_failed = True
3673.6.20 by Malcolm Cleaton
Second review comments
158
                elif result == UploadStatusEnum.REJECTED:
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
159
                    some_rejected = True
160
                else:
161
                    some_accepted = True
3691.117.6 by Malcolm Cleaton
Tidying
162
            except (KeyboardInterrupt, SystemExit):
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
163
                raise
164
            except:
5353.1.4 by Celso Providelo
review comments, r=mwhudson.
165
                self.log.error(
166
                    "Unhandled exception from processing an upload",
167
                    exc_info=True)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
168
                some_failed = True
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
169
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
170
        if some_failed:
171
            destination = "failed"
172
        elif some_rejected:
173
            destination = "rejected"
174
        elif some_accepted:
175
            destination = "accepted"
176
        else:
3673.6.16 by Malcolm Cleaton
Addressed review comments
177
            # There were no changes files at all. We consider
178
            # the upload to be failed in this case.
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
179
            destination = "failed"
180
3673.6.16 by Malcolm Cleaton
Addressed review comments
181
        self.moveUpload(upload_path, destination)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
182
3673.6.16 by Malcolm Cleaton
Addressed review comments
183
    def locateDirectories(self, fsroot):
184
        """List directories in given directory, usually 'incoming'."""
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
185
        # Protecting listdir by a lock ensures that we only get
186
        # completely finished directories listed. See
187
        # PoppyInterface for the other locking place.
5089.1.2 by Celso Providelo
applying review comments, r=kiko.
188
        lockfile_path = os.path.join(fsroot, ".lock")
189
        fsroot_lock = GlobalLock(lockfile_path)
5554.1.2 by Celso Providelo
applying review comments, r=salgado.
190
        mode = stat.S_IMODE(os.stat(lockfile_path).st_mode)
191
6916.1.1 by Curtis Hovey
Fixed comment formats to fidn missing persons, dates, and some bugs.
192
        # XXX cprov 20081024 bug=185731: The lockfile permission can only be
193
        # changed by its owner. Since we can't predict which process will
194
        # create it in production systems we simply ignore errors when trying
195
        # to grant the right permission. At least, one of the process will
196
        # be able to do so.
5554.1.1 by Celso Providelo
Fixing share lockfile issue between poppy (lp_upload) and process-upload (lp_queue). Ignoring exceptions raised while trying to grant the write permission to the group.
197
        try:
198
            os.chmod(lockfile_path, mode | stat.S_IWGRP)
5554.1.2 by Celso Providelo
applying review comments, r=salgado.
199
        except OSError, err:
200
            self.log.debug('Could not fix the lockfile permission: %s' % err)
5089.1.2 by Celso Providelo
applying review comments, r=kiko.
201
3673.6.16 by Malcolm Cleaton
Addressed review comments
202
        try:
3691.348.5 by kiko
I changed the semantics for acquire() to be non-blocking by default, so make it explicit in these callsites
203
            fsroot_lock.acquire(blocking=True)
3673.6.16 by Malcolm Cleaton
Addressed review comments
204
            dir_names = os.listdir(fsroot)
205
        finally:
5089.1.2 by Celso Providelo
applying review comments, r=kiko.
206
            # Skip lockfile deletion, see similar code in poppyinterface.py.
207
            fsroot_lock.release(skip_delete=True)
3673.6.16 by Malcolm Cleaton
Addressed review comments
208
209
        dir_names = [dir_name for dir_name in dir_names if
210
                     os.path.isdir(os.path.join(fsroot, dir_name))]
211
        return dir_names
212
213
    def locateChangesFiles(self, upload_path):
214
        """Locate .changes files in the given upload directory.
215
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
216
        Return .changes files sorted with *_source.changes first. This
217
        is important to us, as in an upload containing several changes files,
218
        it's possible the binary ones will depend on the source ones, so
219
        the source ones should always be considered first.
3673.6.16 by Malcolm Cleaton
Addressed review comments
220
        """
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
221
        changes_files = []
3691.443.60 by Celso Providelo
merge from RF, bunch of conflicts fixed. storing upload_archive in the policy.
222
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
223
        for dirpath, dirnames, filenames in os.walk(upload_path):
3691.443.60 by Celso Providelo
merge from RF, bunch of conflicts fixed. storing upload_archive in the policy.
224
            relative_path = dirpath[len(upload_path) + 1:]
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
225
            for filename in filenames:
226
                if filename.endswith(".changes"):
5353.1.4 by Celso Providelo
review comments, r=mwhudson.
227
                    changes_files.append(
228
                        os.path.join(relative_path, filename))
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
229
        return self.orderFilenames(changes_files)
230
3673.6.16 by Malcolm Cleaton
Addressed review comments
231
    def processChangesFile(self, upload_path, changes_file):
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
232
        """Process a single changes file.
233
3673.6.16 by Malcolm Cleaton
Addressed review comments
234
        This is done by obtaining the appropriate upload policy (according
235
        to command-line options and the value in the .distro file beside
236
        the upload, if present), creating a NascentUpload object and calling
237
        its process method.
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
238
239
        We obtain the context for this processing from the relative path,
240
        within the upload folder, of this changes file. This influences
241
        our creation both of upload policy and the NascentUpload object.
3673.6.16 by Malcolm Cleaton
Addressed review comments
242
243
        See nascentupload.py for the gory details.
244
3691.117.1 by Malcolm Cleaton
Fix and test for 35965. Also first functional (non-doc) test for uploading, watch for more complete support for this in the future.
245
        Returns a value from UploadStatusEnum, or re-raises an exception
246
        from NascentUpload.
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
247
        """
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
248
        # Calculate the distribution from the path within the upload
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
249
        # Reject the upload since we could not process the path,
250
        # Store the exception information as a rejection message.
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
251
        relative_path = os.path.dirname(changes_file)
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
252
        error = None
253
        try:
5353.1.4 by Celso Providelo
review comments, r=mwhudson.
254
            (distribution, suite_name,
255
             archive) = self.getDistributionAndArchive(relative_path)
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
256
        except UploadPathError, e:
257
            # pick some defaults to create the NascentUploap() object.
258
            # We will be rejecting the upload so it doesn matter much.
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
259
            distribution = getUtility(IDistributionSet)['ubuntu']
260
            suite_name = None
261
            archive = distribution.main_archive
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
262
            error = str(e)
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
263
        except PPAUploadPathError, e:
264
            # Again, pick some defaults but leave a hint for the rejection
265
            # emailer that it was a PPA failure.
266
            distribution = getUtility(IDistributionSet)['ubuntu']
267
            suite_name = None
6500.1.11 by Celso Providelo
Fixing test failure with a evil hack, God forbids.
268
            # XXX cprov 20071212: using the first available PPA is not exactly
5353.1.1 by Celso Providelo
Fixing bug 172377 (adding 'X-Launchpad-PPA' header in PPA upload notifications).
269
            # fine because it can confuse the code that sends rejection
270
            # messages if it relies only on archive.purpose (which should be
271
            # enough). On the other hand if we set an arbitrary owner it
272
            # will break nascentupload ACL calculations.
6500.1.11 by Celso Providelo
Fixing test failure with a evil hack, God forbids.
273
            archive = distribution.getAllPPAs()[0]
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
274
            error = str(e)
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
275
276
        self.log.debug("Finding fresh policy")
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
277
        self.options.distro = distribution.name
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
278
        policy = findPolicyByOptions(self.options)
3691.443.60 by Celso Providelo
merge from RF, bunch of conflicts fixed. storing upload_archive in the policy.
279
        policy.archive = archive
4285.2.8 by Mark Shuttleworth
Test fixes for archive uploader
280
        # DistroSeries overriding respect the following precedence:
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
281
        #  1. process-upload.py command-line option (-r),
282
        #  2. upload path,
283
        #  3. changesfile 'Distribution' field.
284
        if suite_name is not None:
4285.2.8 by Mark Shuttleworth
Test fixes for archive uploader
285
            policy.setDistroSeriesAndPocket(suite_name)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
286
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
287
        # The path we want for NascentUpload is the path to the folder
288
        # containing the changes file (and the other files referenced by it).
3804.1.15 by Celso Providelo
code review, pending tests for NUF and changes in publication lookup.
289
        changesfile_path = os.path.join(upload_path, changes_file)
290
        upload = NascentUpload(changesfile_path, policy, self.log)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
291
3691.443.62 by Celso Providelo
merge from RF, conflict solving.
292
        # Store archive lookup error in the upload if it was the case.
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
293
        if error is not None:
3691.443.62 by Celso Providelo
merge from RF, conflict solving.
294
            upload.reject(error)
295
4162.3.1 by Celso Providelo
Fix #110576 (security uploads are broken in nu-cataclysm). More precise tests for security uploads.
296
        # Store processed NascentUpload instance, mostly used for tests.
297
        self.last_processed_upload = upload
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
298
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
299
        try:
3804.1.8 by Celso Providelo
nascentupload.txt fixed, still having problems with archivepublisher.ftests.test_uploadprocessor.
300
            self.log.info("Processing upload %s" % upload.changes.filename)
3673.6.20 by Malcolm Cleaton
Second review comments
301
            result = UploadStatusEnum.ACCEPTED
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
302
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
303
            try:
304
                upload.process()
305
            except UploadPolicyError, e:
306
                upload.reject("UploadPolicyError escaped upload.process: "
307
                              "%s " % e)
308
                self.log.debug("UploadPolicyError escaped upload.process",
309
                               exc_info=True)
3806.2.11 by kiko
Cleanups and move some code around, no functional changes.
310
            except FatalUploadError, e:
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
311
                upload.reject("UploadError escaped upload.process: %s" % e)
312
                self.log.debug("UploadError escaped upload.process",
313
                               exc_info=True)
3691.117.6 by Malcolm Cleaton
Tidying
314
            except (KeyboardInterrupt, SystemExit):
3691.117.1 by Malcolm Cleaton
Fix and test for 35965. Also first functional (non-doc) test for uploading, watch for more complete support for this in the future.
315
                raise
4936.3.4 by Julian Edwards
Improve the API for error checking and early rejection.
316
            except EarlyReturnUploadError:
317
                # An error occurred that prevented further error collection,
318
                # add this fact to the list of errors.
319
                upload.reject(
320
                    "Further error processing not possible because of "
321
                    "a critical previous error.")
3691.117.1 by Malcolm Cleaton
Fix and test for 35965. Also first functional (non-doc) test for uploading, watch for more complete support for this in the future.
322
            except Exception, e:
323
                # In case of unexpected unhandled exception, we'll
324
                # *try* to reject the upload. This may fail and cause
325
                # a further exception, depending on the state of the
326
                # nascentupload objects. In that case, we've lost nothing,
327
                # the new exception will be handled by the caller just like
328
                # the one we caught would have been, by failing the upload
329
                # with no email.
3691.148.1 by Malcolm Cleaton
Fix bad use of log.exception
330
                self.log.exception("Unhandled exception processing upload")
3691.117.1 by Malcolm Cleaton
Fix and test for 35965. Also first functional (non-doc) test for uploading, watch for more complete support for this in the future.
331
                upload.reject("Unhandled exception processing upload: %s" % e)
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
332
4664.1.1 by Curtis Hovey
Normalized comments for bug 3732.
333
            # XXX julian 2007-05-25 bug=29744:
4204.2.19 by Julian Edwards
Add an XXX that I forgot about from the last commital.
334
            # When bug #29744 is fixed (zopeless mails should only be sent
335
            # when transaction is committed) this will cause any emails sent
336
            # sent by do_reject to be lost.
4310.2.4 by Julian Edwards
Amend readability of some code as per the review.
337
            notify = True
338
            if self.options.dryrun or self.options.nomails:
339
                notify = False
3804.1.8 by Celso Providelo
nascentupload.txt fixed, still having problems with archivepublisher.ftests.test_uploadprocessor.
340
            if upload.is_rejected:
3673.6.20 by Malcolm Cleaton
Second review comments
341
                result = UploadStatusEnum.REJECTED
4310.2.3 by Julian Edwards
Add a notification flag to the do_reject call.
342
                upload.do_reject(notify)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
343
                self.ztm.abort()
344
            else:
4310.2.1 by Julian Edwards
Add a boolean flag "notify" to NascentUpload.do_accept() to say whether to
345
                successful = upload.do_accept(notify=notify)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
346
                if not successful:
3673.6.20 by Malcolm Cleaton
Second review comments
347
                    result = UploadStatusEnum.REJECTED
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
348
                    self.log.info("Rejection during accept. "
349
                                  "Aborting partial accept.")
350
                    self.ztm.abort()
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
351
4285.3.67 by Celso Providelo
Include rejection reasons in the uploader log output.
352
            if upload.is_rejected:
353
                self.log.warn("Upload was rejected:")
354
                for msg in upload.rejections:
355
                    self.log.warn("\t%s" % msg)
356
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
357
            if self.options.dryrun:
358
                self.log.info("Dry run, aborting transaction.")
359
                self.ztm.abort()
360
            else:
361
                self.log.info("Committing the transaction and any mails "
362
                              "associated with this upload.")
363
                self.ztm.commit()
364
        except:
365
            self.ztm.abort()
366
            raise
367
3673.6.16 by Malcolm Cleaton
Addressed review comments
368
        return result
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
369
3673.6.16 by Malcolm Cleaton
Addressed review comments
370
    def moveUpload(self, upload, subdir_name):
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
371
        """Move the upload to the named subdir of the root, eg 'accepted'.
372
3673.6.16 by Malcolm Cleaton
Addressed review comments
373
        This includes moving the given upload directory and moving the
374
        matching .distro file, if it exists.
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
375
        """
376
        if self.options.keep or self.options.dryrun:
377
            self.log.debug("Keeping contents untouched")
378
            return
379
3673.6.16 by Malcolm Cleaton
Addressed review comments
380
        pathname = os.path.basename(upload)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
381
382
        target_path = os.path.join(
383
            self.options.base_fsroot, subdir_name, pathname)
3673.6.16 by Malcolm Cleaton
Addressed review comments
384
        self.log.debug("Moving upload directory %s to %s" %
385
            (upload, target_path))
3804.1.12 by Celso Providelo
Fixing soyuz-set-of-upload and related code.
386
        shutil.move(upload, target_path)
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
387
3673.6.16 by Malcolm Cleaton
Addressed review comments
388
        distro_filename = upload + ".distro"
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
389
        if os.path.isfile(distro_filename):
390
            target_path = os.path.join(self.options.base_fsroot, subdir_name,
391
                                       os.path.basename(distro_filename))
392
            self.log.debug("Moving distro file %s to %s" % (distro_filename,
393
                                                            target_path))
3804.1.12 by Celso Providelo
Fixing soyuz-set-of-upload and related code.
394
            shutil.move(distro_filename, target_path)
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
395
3673.6.1 by Malcolm Cleaton
Tidying process-upload script and tests
396
    def orderFilenames(self, fnames):
397
        """Order filenames, sorting *_source.changes before others.
398
399
        Aside from that, a standard string sort.
400
        """
3673.6.22 by Malcolm Cleaton
Argh, TABS!!
401
        def sourceFirst(filename):
402
            return (not filename.endswith("_source.changes"), filename)
3673.6.16 by Malcolm Cleaton
Addressed review comments
403
404
        return sorted(fnames, key=sourceFirst)
405
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
406
    def getDistributionAndArchive(self, relative_path):
407
        """Locate the distribution and archive for the upload.
408
409
        We do this by analysing the path to which the user has uploaded,
410
        ie. the relative path within the upload folder to the changes file.
411
412
        The valid paths are:
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
413
        '' - default distro, ubuntu
414
        '<distroname>' - given distribution
7465.2.2 by Celso Providelo
Revert RF 7436.
415
        '~<personname>/<distroname>/[distroseriesname]' - given ppa,
416
          distribution and optionally a distroseries.
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
417
418
        I raises UploadPathError if something was wrong when parsing it.
419
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
420
        On success it returns a tuple of IDistribution, suite-name,
421
        IArchive for the given path, where the second field can be None.
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
422
        """
423
        parts = relative_path.split(os.path.sep)
3691.443.50 by Celso Providelo
Fix #88611 (only one PPA per user) and simpler upload/publish paths for PPA.
424
        first_path = parts[0]
425
4285.2.8 by Mark Shuttleworth
Test fixes for archive uploader
426
        # Empty distroseries override by default.
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
427
        suite_name = None
428
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
429
        # Distribution name only, or nothing
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
430
        if len(parts) == 1:
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
431
            distribution_name = first_path
3691.443.50 by Celso Providelo
Fix #88611 (only one PPA per user) and simpler upload/publish paths for PPA.
432
            # fallback to ubuntu
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
433
            if not distribution_name:
434
                distribution_name = 'ubuntu'
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
435
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
436
            distribution = getUtility(IDistributionSet).getByName(
437
                distribution_name)
438
            if not distribution:
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
439
                raise UploadPathError(
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
440
                    "Could not find distribution '%s'" % distribution_name)
441
            archive = distribution.main_archive
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
442
7465.2.2 by Celso Providelo
Revert RF 7436.
443
        # PPA upload (~<person>/<distro>/[distroseries])
444
        elif len(parts) <= 3:
3691.443.50 by Celso Providelo
Fix #88611 (only one PPA per user) and simpler upload/publish paths for PPA.
445
            if not first_path.startswith('~'):
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
446
                raise PPAUploadPathError(
3691.443.50 by Celso Providelo
Fix #88611 (only one PPA per user) and simpler upload/publish paths for PPA.
447
                    "PPA upload path must start with '~'.")
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
448
449
            # Skip over ~
3691.443.50 by Celso Providelo
Fix #88611 (only one PPA per user) and simpler upload/publish paths for PPA.
450
            person_name = first_path[1:]
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
451
            person = getUtility(IPersonSet).getByName(person_name)
452
            if person is None:
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
453
                raise PPAUploadPathError(
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
454
                    "Could not find person '%s'" % person_name)
3691.446.1 by Celso Providelo
Incorporate 'path-aware' upload processing as part of PPA upload support, doc/soyuz-upload.txt & doc/soyuz-set-of-uploads.txt are busted.
455
7465.2.2 by Celso Providelo
Revert RF 7436.
456
            distribution_name = parts[1]
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
457
            distribution = getUtility(IDistributionSet).getByName(
458
                distribution_name)
459
            if distribution is None:
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
460
                raise PPAUploadPathError(
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
461
                    "Could not find distribution '%s'" % distribution_name)
3691.443.50 by Celso Providelo
Fix #88611 (only one PPA per user) and simpler upload/publish paths for PPA.
462
4285.3.32 by Celso Providelo
Do not create new PPA on successfully uploads (it's required to create them via the web UI). Deny uploads for disabled PPAs. Minor style fixing in uploadprocessor.py. Tests inside (tm).
463
            archive = person.archive
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
464
            if archive is None:
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
465
                raise PPAUploadPathError(
3691.443.50 by Celso Providelo
Fix #88611 (only one PPA per user) and simpler upload/publish paths for PPA.
466
                    "Could not find PPA for '%s'" % person_name)
467
4285.3.32 by Celso Providelo
Do not create new PPA on successfully uploads (it's required to create them via the web UI). Deny uploads for disabled PPAs. Minor style fixing in uploadprocessor.py. Tests inside (tm).
468
            if not archive.enabled:
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
469
                raise PPAUploadPathError(
4285.3.32 by Celso Providelo
Do not create new PPA on successfully uploads (it's required to create them via the web UI). Deny uploads for disabled PPAs. Minor style fixing in uploadprocessor.py. Tests inside (tm).
470
                    "%s is disabled" % archive.title)
471
4285.3.42 by Celso Providelo
moving PPA handlers to IDistribution, improving IArchive tests, using IArchive.distribution in Publisher System, verifying Archive.distribution in upload time. *some* tests missing (tm).
472
            if archive.distribution != distribution:
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
473
                raise PPAUploadPathError(
4285.3.42 by Celso Providelo
moving PPA handlers to IDistribution, improving IArchive tests, using IArchive.distribution in Publisher System, verifying Archive.distribution in upload time. *some* tests missing (tm).
474
                    "%s only supports uploads to '%s'"
475
                    % (archive.title, archive.distribution.name))
476
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
477
            if len(parts) > 2:
7465.2.2 by Celso Providelo
Revert RF 7436.
478
                suite_name = parts[2]
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
479
                # Check if the given suite name is valid.
480
                # We will return the suite_name string simply.
481
                try:
4285.2.8 by Mark Shuttleworth
Test fixes for archive uploader
482
                    suite = distribution.getDistroSeriesAndPocket(suite_name)
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
483
                except NotFoundError:
4810.3.1 by Julian Edwards
PPA upload emails should go to the uploader (and hence package signer) only.
484
                    raise PPAUploadPathError(
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
485
                        "Could not find suite '%s'" % suite_name)
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
486
        else:
487
            raise UploadPathError(
7465.2.2 by Celso Providelo
Revert RF 7436.
488
                "Path mismatch '%s'. Use ~<person>/<distro>/[distroseries]/"
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
489
                "[files] for PPAs and <distro>/[files] for normal uploads."
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
490
                % (relative_path))
491
3691.443.95 by Celso Providelo
implementing path-based suite(distrorelease) overriding for PPA uploads.
492
        return (distribution, suite_name, archive)
3691.443.31 by Celso Providelo
fix test_uploadprocessor and related code.
493