~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

# pylint: disable-msg=E0211,E0213

"""BinaryPackageBuild interfaces."""

__metaclass__ = type

__all__ = [
    'BuildSetStatus',
    'CannotBeRescored',
    'IBinaryPackageBuild',
    'IBuildRescoreForm',
    'IBinaryPackageBuildSet',
    'UnparsableDependencies',
    ]

import httplib

from lazr.enum import (
    EnumeratedType,
    Item,
    )
from lazr.restful.declarations import (
    error_status,
    export_as_webservice_entry,
    export_write_operation,
    exported,
    operation_for_version,
    operation_parameters,
    )
from lazr.restful.fields import Reference
from zope.interface import (
    Attribute,
    Interface,
    )
from zope.schema import (
    Bool,
    Int,
    Text,
    TextLine,
    )

from lp import _
from lp.buildmaster.enums import BuildStatus
from lp.buildmaster.interfaces.buildfarmjob import ISpecificBuildFarmJobSource
from lp.buildmaster.interfaces.packagebuild import IPackageBuild
from lp.soyuz.interfaces.processor import IProcessor
from lp.soyuz.interfaces.publishing import ISourcePackagePublishingHistory
from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease


@error_status(httplib.BAD_REQUEST)
class CannotBeRescored(Exception):
    """Raised when rescoring a build that cannot be rescored."""
    _message_prefix = "Cannot rescore build"


class UnparsableDependencies(Exception):
    """Raised when parsing invalid dependencies on a binary package."""


class IBinaryPackageBuildView(IPackageBuild):
    """A Build interface for items requiring launchpad.View."""
    id = Int(title=_('ID'), required=True, readonly=True)

    package_build = Reference(
        title=_('Package build'), schema=IPackageBuild, required=True,
        readonly=True, description=_('The base package build'))

    # Overridden from IBuildFarmJob to ensure required is True.
    processor = Reference(
        title=_("Processor"), schema=IProcessor,
        required=True, readonly=True,
        description=_("The Processor where this build should be built."))

    source_package_release = Reference(
        title=_('Source'), schema=ISourcePackageRelease,
        required=True, readonly=True,
        description=_("The SourcePackageRelease requested to build."))

    distro_arch_series = Reference(
        title=_("Architecture"),
        # Really IDistroArchSeries
        schema=Interface,
        required=True, readonly=True,
        description=_("The DistroArchSeries context for this build."))

    # Properties
    current_source_publication = exported(
        Reference(
            title=_("Source publication"),
            schema=ISourcePackagePublishingHistory,
            required=False, readonly=True,
            description=_("The current source publication for this build.")))

    distro_series = Attribute("Direct parent needed by CanonicalURL")
    arch_tag = exported(
        Text(title=_("Architecture tag"), required=False))
    distributionsourcepackagerelease = Attribute("The page showing the "
        "details for this sourcepackagerelease in this distribution.")
    binarypackages = Attribute(
        "A list of binary packages that resulted from this build, "
        "not limited and ordered by name.")
    distroarchseriesbinarypackages = Attribute(
        "A list of distroarchseriesbinarypackages that resulted from this"
        "build, ordered by name.")

    can_be_rescored = exported(
        Bool(
            title=_("Can Be Rescored"), required=False, readonly=True,
            description=_(
                "Whether or not this build record can be rescored "
                "manually.")))

    can_be_retried = exported(
        Bool(
            title=_("Can Be Retried"), required=False, readonly=True,
            description=_(
                "Whether or not this build record can be retried.")))

    can_be_cancelled = exported(
        Bool(
            title=_("Can Be Cancelled"), required=False, readonly=True,
            description=_(
                "Whether or not this build record can be cancelled.")))

    is_virtualized = Attribute(
        "Whether or not this build requires a virtual build host or not.")

    upload_changesfile = Attribute(
        "The `LibraryFileAlias` object containing the changes file which "
        "was originally uploaded with the results of this build. It's "
        "'None' if it is build imported by Gina.")

    changesfile_url = exported(
        TextLine(
            title=_("Changes File URL"), required=False,
            description=_("The URL for the changes file for this build. "
                          "Will be None if the build was imported by Gina.")))

    package_upload = Attribute(
        "The `PackageUpload` record corresponding to the original upload "
        "of the binaries resulted from this build. It's 'None' if it is "
        "a build imported by Gina.")

    api_score = exported(
        Int(
            title=_("Score of the related job (if any)"),
            readonly=True,
            ),
        exported_as="score")

    def updateDependencies():
        """Update the build-dependencies line within the targeted context."""

    def __getitem__(name):
        """Mapped to getBinaryPackageRelease."""

    def getBinaryPackageRelease(name):
        """Return the binary package from this build with the given name, or
        raise NotFoundError if no such package exists.
        """

    def createBinaryPackageRelease(
        binarypackagename, version, summary, description, binpackageformat,
        component, section, priority, installedsize, architecturespecific,
        shlibdeps=None, depends=None, recommends=None, suggests=None,
        conflicts=None, replaces=None, provides=None, pre_depends=None,
        enhances=None, breaks=None, essential=False, debug_package=None,
        user_defined_fields=None, homepage=None):
        """Create and return a `BinaryPackageRelease`.

        The binarypackagerelease will be attached to this specific build.
        """

    def getFileByName(filename):
        """Return the corresponding `ILibraryFileAlias` in this context.

        The following file types (and extension) can be looked up in the
        archive context:

         * Binary changesfile: '.changes';
         * Build logs: '.txt.gz';
         * Build upload logs: '_log.txt';

        :param filename: exactly filename to be looked up.

        :raises AssertionError if the given filename contains a unsupported
            filename and/or extension, see the list above.
        :raises NotFoundError if no file could not be found.

        :return the corresponding `ILibraryFileAlias` if the file was found.
        """

    def getBinaryPackageFileByName(filename):
        """Return the corresponding `IBinaryPackageFile` in this context.

        :param filename: the filename to look up.
        :return: the corresponding `IBinaryPackageFile` if it was found.
        """

    def getBinaryPackageNamesForDisplay():
        """Retrieve the build's binary package names for display purposes.

        :return: a result set of
            (`IBinaryPackageRelease`, `IBinaryPackageName`) ordered by name
            and `IBinaryPackageRelease.id`.
        """

    def getBinaryFilesForDisplay():
        """Retrieve the build's `IBinaryPackageFile`s for display purposes.

        Also prefetches other related objects needed for display.

        :return: a result set of (`IBinaryPackageRelease`,
            `IBinaryPackageFile`, `ILibraryFileAlias`, `ILibraryFileContent`).
        """


class IBinaryPackageBuildEdit(Interface):
    """A Build interface for items requiring launchpad.Edit."""

    @export_write_operation()
    def retry():
        """Restore the build record to its initial state.

        Build record loses its history, is moved to NEEDSBUILD and a new
        non-scored BuildQueue entry is created for it.
        """

    @export_write_operation()
    @operation_for_version("devel")
    def cancel():
        """Cancel the build if it is either pending or in progress.

        Call the can_be_cancelled() method prior to this one to find out if
        cancelling the build is possible.

        If the build is in progress, it is marked as CANCELLING until the
        buildd manager terminates the build and marks it CANCELLED. If the
        build is not in progress, it is marked CANCELLED immediately and is
        removed from the build queue.

        If the build is not in a cancellable state, this method is a no-op.
        """


class IBinaryPackageBuildAdmin(Interface):
    """A Build interface for items requiring launchpad.Admin."""

    @operation_parameters(score=Int(title=_("Score"), required=True))
    @export_write_operation()
    def rescore(score):
        """Change the build's score."""


class IBinaryPackageBuild(
    IBinaryPackageBuildView, IBinaryPackageBuildEdit,
    IBinaryPackageBuildAdmin):
    """A Build interface"""
    export_as_webservice_entry(singular_name='build', plural_name='builds')


class BuildSetStatus(EnumeratedType):
    """`IBuildSet` status type

    Builds exist in the database in a number of states such as 'complete',
    'needs build' and 'dependency wait'. We sometimes provide a summary
    status of a set of builds.
    """
    # Until access to the name, title and description of exported types
    # is available through the API, set the title of these statuses
    # to match the name. This enables the result of API calls (which is
    # currently the title) to be used programatically (for example, as a
    # css class name).
    NEEDSBUILD = Item(
        title='NEEDSBUILD',  # "Need building",
        description='There are some builds waiting to be built.')

    FULLYBUILT_PENDING = Item(
        title='FULLYBUILT_PENDING',
        description="All builds were built successfully but have not yet "
                    "been published.")

    FULLYBUILT = Item(title='FULLYBUILT',  # "Successfully built",
                      description="All builds were built successfully.")

    FAILEDTOBUILD = Item(title='FAILEDTOBUILD',  # "Failed to build",
                         description="There were build failures.")

    BUILDING = Item(title='BUILDING',  # "Currently building",
                    description="There are some builds currently building.")


class IBinaryPackageBuildSet(ISpecificBuildFarmJobSource):
    """Interface for BinaryPackageBuildSet"""

    def new(distro_arch_series, source_package_release, processor,
            archive, pocket, status=BuildStatus.NEEDSBUILD,
            date_created=None):
        """Create a new `IBinaryPackageBuild`.

        :param distro_arch_series: An `IDistroArchSeries`.
        :param source_package_release: An `ISourcePackageRelease`.
        :param processor: An `IProcessor`.
        :param archive: An `IArchive` in which context the build is built.
        :param pocket: An item of `PackagePublishingPocket`.
        :param status: A `BuildStatus` item indicating the builds status.
        :param date_created: An optional datetime to ensure multiple builds
            in the same transaction don't all get the same UTC_NOW.
        """

    def getBuildBySRAndArchtag(sourcepackagereleaseID, archtag):
        """Return a build for a SourcePackageRelease and an ArchTag"""

    def getPendingBuildsForArchSet(archseries):
        """Return all pending build records within a group of ArchSeries

        Pending means that buildstate is NEEDSBUILD.
        """

    def getBuildsForBuilder(builder_id, status=None, name=None,
                            arch_tag=None):
        """Return build records touched by a builder.

        :param builder_id: The id of the builder for which to find builds.
        :param status: If status is provided, only builds with that status
            will be returned.
        :param name: If name is provided, only builds which correspond to a
            matching sourcepackagename will be returned (SQL LIKE).
        :param arch_tag: If arch_tag is provided, only builds for that
            architecture will be returned.
        :return: a `ResultSet` representing the requested builds.
        """

    def getBuildsForArchive(archive, status=None, name=None, pocket=None,
                            arch_tag=None):
        """Return build records targeted to a given IArchive.

        :param archive: The archive for which builds will be returned.
        :param status: If status is provided, only builders with that
            status will be returned.
        :param name: If name is passed, return only build which the
            sourcepackagename matches (SQL LIKE).
        :param pocket: If pocket is provided only builds for that pocket
            will be returned.
        :param arch_tag: If arch_tag is provided, only builds for that
            architecture will be returned.
        :return: a `ResultSet` representing the requested builds.
        """

    def getBuildsByArchIds(distribution, arch_ids, status=None, name=None,
                           pocket=None):
        """Retrieve Build Records for a given arch_ids list.

        Optionally, for a given status and/or pocket, if ommited return all
        records. If name is passed return only the builds which the
        sourcepackagename matches (SQL LIKE).
        """

    def retryDepWaiting(distroarchseries):
        """Re-process all MANUALDEPWAIT builds for a given IDistroArchSeries.

        This method will update all the dependency lines of all MANUALDEPWAIT
        records in the given architecture and those with all dependencies
        satisfied at this point will be automatically retried and re-scored.
        """

    def getBuildsBySourcePackageRelease(sourcepackagerelease_ids,
                                        buildstate=None):
        """Return all builds related with the given list of source releases.

        Eager loads the PackageBuild and BuildFarmJob records for the builds.

        :param sourcepackagerelease_ids: list of `ISourcePackageRelease`s;
        :param buildstate: option build state filter.

        :return: a list of `IBuild` records not target to PPA archives.
        """

    def getStatusSummaryForBuilds(builds):
        """Return a summary of the build status for the given builds.

        The returned summary includes a status, a description of
        that status and the builds related to the status.

        :param builds: A list of build records.
        :type builds: ``list``
        :return: A dict consisting of the build status summary for the
            given builds. For example:
                {
                    'status': BuildSetStatus.FULLYBUILT,
                    'builds': [build1, build2]
                }
            or, an example where there are currently some builds building:
                {
                    'status': BuildSetStatus.BUILDING,
                    'builds':[build3]
                }
        :rtype: ``dict``.
        """

    def getByQueueEntry(queue_entry):
        """Return an IBuild instance for the given build queue entry.

        Retrieve the only one possible build record associated with the given
        build queue entry. If not found, return None.
        """

    def getQueueEntriesForBuildIDs(build_ids):
        """Return the IBuildQueue instances for the IBuild IDs at hand.

        Retrieve the build queue and related builder rows associated with the
        builds in question where they exist.
        """

    def calculateCandidates(archseries):
        """Return the BuildQueue records for the given archseries's Builds.

        Returns a selectRelease of BuildQueue items for sorted by descending
        'lastscore' for Builds within the given archseries.

        'archseries' argument should be a list of DistroArchSeries and it is
        asserted to not be None/empty.
        """

    def preloadBuildsData(builds):
        """Prefetch the data related to the builds.

        """


class IBuildRescoreForm(Interface):
    """Form for rescoring a build."""

    priority = Int(
        title=_("Priority"), required=True, min=-2 ** 31, max=2 ** 31,
        description=_("Build priority, the build with the highest value will "
                      "be dispatched first."))