Merge lp:~michael.nelson/launchpad/db-build-farm-job-model into lp:launchpad/db-devel
- db-build-farm-job-model
- Merge into db-devel
Status: | Merged |
---|---|
Approved by: | Michael Nelson |
Approved revision: | no longer in the source branch. |
Merged at revision: | 9405 |
Proposed branch: | lp:~michael.nelson/launchpad/db-build-farm-job-model |
Merge into: | lp:launchpad/db-devel |
Prerequisite: | lp:~michael.nelson/launchpad/db-build-generalisation-db-changes |
Diff against target: |
484 lines (+327/-37) 6 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+5/-0) lib/lp/buildmaster/configure.zcml (+15/-0) lib/lp/buildmaster/interfaces/buildfarmjob.py (+82/-14) lib/lp/buildmaster/model/buildfarmjob.py (+102/-22) lib/lp/buildmaster/model/packagebuildfarmjob.py (+3/-1) lib/lp/buildmaster/tests/test_buildfarmjob.py (+120/-0) |
To merge this branch: | bzr merge lp:~michael.nelson/launchpad/db-build-farm-job-model |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij (community) | Abstain | ||
Abel Deuring (community) | code | Approve | |
Review via email: mp+23913@code.launchpad.net |
Commit message
Description of the change
This branch is part of a pipeline for
https:/
https:/
In particular, this branch turns BuildFarmJob into a concrete class, while at the same time, continuing support temporarily for classes that still expect this to be an in-memory object.
It is dependent on the pending schema patch in a previous branch.
To test
=======
First update the test db schema (required as the db patch still needs to be updated to remove the old build table):
psql launchpad_
bin/py database/
And then:
bin/test -vvt test_buildfarmjob -t doc/build.txt -t test_buildqueue -t test_sourcepack
The next stage will be a similar conversion for PackageBuildFar
Abel Deuring (adeuring) wrote : | # |
Hi Michael,
a nice branch; just just have a few formal nitpicks.
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -9,15 +9,20 @@
>
> __all__ = [
> 'IBuildFarmJob',
> + 'IBuildFarmJobS
> 'IBuildFarmJobD
> 'BuildFarmJobType',
> ]
>
> from zope.interface import Interface, Attribute
> -
> -from canonical.launchpad import _
> +from zope.schema import Bool, Choice, Datetime
> from lazr.enum import DBEnumeratedType, DBItem
> from lazr.restful.fields import Reference
> +
> +from canonical.launchpad import _
> +from canonical.
> +
> +from lp.buildmaster.
> from lp.soyuz.
>
>
> @@ -56,6 +61,66 @@
> class IBuildFarmJob(
> """Operations that jobs for the build farm must implement."""
>
> + id = Attribute('The build farm job ID.')
> +
> + processor = Reference(
> + IProcessor, title=_
> + description=_(
> + "The Processor required by this build farm job. "
> + "For processor-
It is perhaps my limited English knowledge, but this sounds to me
like a polite request to the implementation class to do the right
thing ;) What about "should|must be None for processor-
> +
> + virtualized = Bool(
> + title=_
> + description=_(
> + "The virtualization setting required by this build farm job. "
> + "For job types that do not care about virtualization please "
> + "return None."))
Same here.
> +
> + date_created = Datetime(
> + title=_("Date created"), required=True, readonly=True,
> + description=_("The timestamp when the build farm job was created."))
> +
> + date_started = Datetime(
> + title=_("Date started"), required=False, readonly=True,
> + description=_("The timestamp when the build farm job was started."))
> +
> + date_finished = Datetime(
> + title=_("Date finished"), required=False, readonly=True,
> + description=_("The timestamp when the build farm job was finished."))
> +
> + date_first_
> + title=_("Date finished"), required=False, readonly=True,
> + description=_("The timestamp when the build farm job was finished."))
s/finished/
> +
> + builder = Reference(
> + title=_("Builder"), schema=IBuilder, required=False, readonly=True,
> + description=_("The builder assigned to this job."))
> +
> + status = Choice(
> + title=_('Status'), required=True,
> + # Really PackagePublishi
s/PackagePublis
[...]
> @@ -149,3 +202,17 @@
> accurately based on this job's properties.
> """
>
> +
> +class IBuildFarmJobSo
> + """A utility of Build...
Michael Nelson (michael.nelson) wrote : | # |
On Fri, Apr 23, 2010 at 12:53 PM, Abel Deuring
<email address hidden> wrote:
> Hi Michael,
>
> a nice branch; just just have a few formal nitpicks.
Thanks Abel, comments below.
>
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -9,15 +9,20 @@
>>
>> __all__ = [
>> 'IBuildFarmJob',
>> + 'IBuildFarmJobS
>> 'IBuildFarmJobD
>> 'BuildFarmJobType',
>> ]
>>
>> from zope.interface import Interface, Attribute
>> -
>> -from canonical.launchpad import _
>> +from zope.schema import Bool, Choice, Datetime
>> from lazr.enum import DBEnumeratedType, DBItem
>> from lazr.restful.fields import Reference
>> +
>> +from canonical.launchpad import _
>> +from canonical.
>> +
>> +from lp.buildmaster.
>> from lp.soyuz.
>>
>>
>> @@ -56,6 +61,66 @@
>> class IBuildFarmJob(
>> """Operations that jobs for the build farm must implement."""
>>
>> + id = Attribute('The build farm job ID.')
>> +
>> + processor = Reference(
>> + IProcessor, title=_
>> + description=_(
>> + "The Processor required by this build farm job. "
>> + "For processor-
>
> It is perhaps my limited English knowledge, but this sounds to me
> like a polite request to the implementation class to do the right
> thing ;) What about "should|must be None for processor-
Yep. This was just a copy-n-paste, but you're right. Updated.
>
>
>> +
>> + virtualized = Bool(
>> + title=_
>> + description=_(
>> + "The virtualization setting required by this build farm job. "
>> + "For job types that do not care about virtualization please "
>> + "return None."))
>
> Same here.
Ditto.
>
>> +
>> + date_created = Datetime(
>> + title=_("Date created"), required=True, readonly=True,
>> + description=_("The timestamp when the build farm job was created."))
>> +
>> + date_started = Datetime(
>> + title=_("Date started"), required=False, readonly=True,
>> + description=_("The timestamp when the build farm job was started."))
>> +
>> + date_finished = Datetime(
>> + title=_("Date finished"), required=False, readonly=True,
>> + description=_("The timestamp when the build farm job was finished."))
>> +
>> + date_first_
>> + title=_("Date finished"), required=False, readonly=True,
>> + description=_("The timestamp when the build farm job was finished."))
>
> s/finished/
Done.
>
>> +
>> + builder = Reference(
>> + title=_("Builder"), schema=IBuilder, required=False, readonly=True,
>> + description=_("The builder assigned to this job."))
>> +
>> + status = Choice(
>> + title=_('Status'...
1 | === modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py' | |||
2 | --- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-21 16:08:35 +0000 | |||
3 | +++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-23 12:13:43 +0000 | |||
4 | @@ -67,14 +67,14 @@ | |||
5 | 67 | IProcessor, title=_("Processor"), required=False, readonly=True, | 67 | IProcessor, title=_("Processor"), required=False, readonly=True, |
6 | 68 | description=_( | 68 | description=_( |
7 | 69 | "The Processor required by this build farm job. " | 69 | "The Processor required by this build farm job. " |
9 | 70 | "For processor-independent job types please return None.")) | 70 | "This should be None for processor-independent job types.")) |
10 | 71 | 71 | ||
11 | 72 | virtualized = Bool( | 72 | virtualized = Bool( |
12 | 73 | title=_('Virtualized'), required=False, readonly=True, | 73 | title=_('Virtualized'), required=False, readonly=True, |
13 | 74 | description=_( | 74 | description=_( |
14 | 75 | "The virtualization setting required by this build farm job. " | 75 | "The virtualization setting required by this build farm job. " |
17 | 76 | "For job types that do not care about virtualization please " | 76 | "This should be None for job types that do not care whether " |
18 | 77 | "return None.")) | 77 | "they run virtualized.")) |
19 | 78 | 78 | ||
20 | 79 | date_created = Datetime( | 79 | date_created = Datetime( |
21 | 80 | title=_("Date created"), required=True, readonly=True, | 80 | title=_("Date created"), required=True, readonly=True, |
22 | @@ -90,7 +90,7 @@ | |||
23 | 90 | 90 | ||
24 | 91 | date_first_dispatched = Datetime( | 91 | date_first_dispatched = Datetime( |
25 | 92 | title=_("Date finished"), required=False, readonly=True, | 92 | title=_("Date finished"), required=False, readonly=True, |
27 | 93 | description=_("The timestamp when the build farm job was finished.")) | 93 | description=_("The timestamp when the build farm job was dispatched.")) |
28 | 94 | 94 | ||
29 | 95 | builder = Reference( | 95 | builder = Reference( |
30 | 96 | title=_("Builder"), schema=IBuilder, required=False, readonly=True, | 96 | title=_("Builder"), schema=IBuilder, required=False, readonly=True, |
31 | @@ -98,7 +98,7 @@ | |||
32 | 98 | 98 | ||
33 | 99 | status = Choice( | 99 | status = Choice( |
34 | 100 | title=_('Status'), required=True, | 100 | title=_('Status'), required=True, |
36 | 101 | # Really PackagePublishingPocket, patched in | 101 | # Really BuildStatus, patched in |
37 | 102 | # _schema_circular_imports.py | 102 | # _schema_circular_imports.py |
38 | 103 | vocabulary=DBEnumeratedType, | 103 | vocabulary=DBEnumeratedType, |
39 | 104 | description=_("The current status of the job.")) | 104 | description=_("The current status of the job.")) |
40 | 105 | 105 | ||
41 | === modified file 'lib/lp/buildmaster/model/buildfarmjob.py' | |||
42 | --- lib/lp/buildmaster/model/buildfarmjob.py 2010-04-21 16:08:35 +0000 | |||
43 | +++ lib/lp/buildmaster/model/buildfarmjob.py 2010-04-23 12:20:34 +0000 | |||
44 | @@ -71,10 +71,8 @@ | |||
45 | 71 | job_type = DBEnum( | 71 | job_type = DBEnum( |
46 | 72 | name='job_type', allow_none=False, enum=BuildFarmJobType) | 72 | name='job_type', allow_none=False, enum=BuildFarmJobType) |
47 | 73 | 73 | ||
52 | 74 | def __init__(self, job_type, status=None, processor=None, | 74 | def __init__(self, job_type, status=BuildStatus.NEEDSBUILD, |
53 | 75 | virtualized=None): | 75 | processor=None, virtualized=None): |
50 | 76 | if status is None: | ||
51 | 77 | status = BuildStatus.NEEDSBUILD | ||
54 | 78 | self.job_type, self.status, self.process, self.virtualized = ( | 76 | self.job_type, self.status, self.process, self.virtualized = ( |
55 | 79 | job_type, | 77 | job_type, |
56 | 80 | status, | 78 | status, |
57 | @@ -83,7 +81,7 @@ | |||
58 | 83 | ) | 81 | ) |
59 | 84 | 82 | ||
60 | 85 | @classmethod | 83 | @classmethod |
62 | 86 | def new(cls, job_type, status=None, processor=None, | 84 | def new(cls, job_type, status=BuildStatus.NEEDSBUILD, processor=None, |
63 | 87 | virtualized=None): | 85 | virtualized=None): |
64 | 88 | """See `IBuildFarmJobSource`.""" | 86 | """See `IBuildFarmJobSource`.""" |
65 | 89 | build_farm_job = BuildFarmJob( | 87 | build_farm_job = BuildFarmJob( |
Jelmer Vernooij (jelmer) : | # |
Preview Diff
1 | === modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py' | |||
2 | --- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-04-23 03:16:22 +0000 | |||
3 | +++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-05-05 15:46:51 +0000 | |||
4 | @@ -34,6 +34,7 @@ | |||
5 | 34 | from lp.bugs.interfaces.bugtracker import IBugTracker | 34 | from lp.bugs.interfaces.bugtracker import IBugTracker |
6 | 35 | from lp.bugs.interfaces.bugwatch import IBugWatch | 35 | from lp.bugs.interfaces.bugwatch import IBugWatch |
7 | 36 | from lp.buildmaster.interfaces.buildbase import BuildStatus | 36 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
8 | 37 | from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob | ||
9 | 37 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild | 38 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild |
10 | 38 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords | 39 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
11 | 39 | from lp.blueprints.interfaces.specification import ISpecification | 40 | from lp.blueprints.interfaces.specification import ISpecification |
12 | @@ -271,6 +272,10 @@ | |||
13 | 271 | patch_plain_parameter_type( | 272 | patch_plain_parameter_type( |
14 | 272 | IArchive, 'deletePackagesetUploader', 'packageset', IPackageset) | 273 | IArchive, 'deletePackagesetUploader', 'packageset', IPackageset) |
15 | 273 | 274 | ||
16 | 275 | |||
17 | 276 | # IBuildFarmJob | ||
18 | 277 | IBuildFarmJob['status'].vocabulary = BuildStatus | ||
19 | 278 | |||
20 | 274 | # IDistribution | 279 | # IDistribution |
21 | 275 | IDistribution['series'].value_type.schema = IDistroSeries | 280 | IDistribution['series'].value_type.schema = IDistroSeries |
22 | 276 | patch_reference_property( | 281 | patch_reference_property( |
23 | 277 | 282 | ||
24 | === modified file 'lib/lp/buildmaster/configure.zcml' | |||
25 | --- lib/lp/buildmaster/configure.zcml 2010-03-05 13:52:32 +0000 | |||
26 | +++ lib/lp/buildmaster/configure.zcml 2010-05-05 15:46:51 +0000 | |||
27 | @@ -42,6 +42,21 @@ | |||
28 | 42 | permission="zope.Public"/> | 42 | permission="zope.Public"/> |
29 | 43 | 43 | ||
30 | 44 | 44 | ||
31 | 45 | <!-- BuildFarmJob --> | ||
32 | 46 | <class | ||
33 | 47 | class="lp.buildmaster.model.buildfarmjob.BuildFarmJob"> | ||
34 | 48 | <allow | ||
35 | 49 | interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob" /> | ||
36 | 50 | </class> | ||
37 | 51 | <securedutility | ||
38 | 52 | component="lp.buildmaster.model.buildfarmjob.BuildFarmJob" | ||
39 | 53 | provides="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobSource"> | ||
40 | 54 | |||
41 | 55 | <allow | ||
42 | 56 | interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobSource" /> | ||
43 | 57 | </securedutility> | ||
44 | 58 | |||
45 | 59 | |||
46 | 45 | <!-- BuildQueue --> | 60 | <!-- BuildQueue --> |
47 | 46 | <class | 61 | <class |
48 | 47 | class="lp.buildmaster.model.buildqueue.BuildQueue"> | 62 | class="lp.buildmaster.model.buildqueue.BuildQueue"> |
49 | 48 | 63 | ||
50 | === modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py' | |||
51 | --- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-28 08:24:54 +0000 | |||
52 | +++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-05-05 15:46:51 +0000 | |||
53 | @@ -9,15 +9,20 @@ | |||
54 | 9 | 9 | ||
55 | 10 | __all__ = [ | 10 | __all__ = [ |
56 | 11 | 'IBuildFarmJob', | 11 | 'IBuildFarmJob', |
57 | 12 | 'IBuildFarmJobSource', | ||
58 | 12 | 'IBuildFarmJobDerived', | 13 | 'IBuildFarmJobDerived', |
59 | 13 | 'BuildFarmJobType', | 14 | 'BuildFarmJobType', |
60 | 14 | ] | 15 | ] |
61 | 15 | 16 | ||
62 | 16 | from zope.interface import Interface, Attribute | 17 | from zope.interface import Interface, Attribute |
65 | 17 | 18 | from zope.schema import Bool, Choice, Datetime | |
64 | 18 | from canonical.launchpad import _ | ||
66 | 19 | from lazr.enum import DBEnumeratedType, DBItem | 19 | from lazr.enum import DBEnumeratedType, DBItem |
67 | 20 | from lazr.restful.fields import Reference | 20 | from lazr.restful.fields import Reference |
68 | 21 | |||
69 | 22 | from canonical.launchpad import _ | ||
70 | 23 | from canonical.launchpad.interfaces.librarian import ILibraryFileAlias | ||
71 | 24 | |||
72 | 25 | from lp.buildmaster.interfaces.builder import IBuilder | ||
73 | 21 | from lp.soyuz.interfaces.processor import IProcessor | 26 | from lp.soyuz.interfaces.processor import IProcessor |
74 | 22 | 27 | ||
75 | 23 | 28 | ||
76 | @@ -56,6 +61,66 @@ | |||
77 | 56 | class IBuildFarmJob(Interface): | 61 | class IBuildFarmJob(Interface): |
78 | 57 | """Operations that jobs for the build farm must implement.""" | 62 | """Operations that jobs for the build farm must implement.""" |
79 | 58 | 63 | ||
80 | 64 | id = Attribute('The build farm job ID.') | ||
81 | 65 | |||
82 | 66 | processor = Reference( | ||
83 | 67 | IProcessor, title=_("Processor"), required=False, readonly=True, | ||
84 | 68 | description=_( | ||
85 | 69 | "The Processor required by this build farm job. " | ||
86 | 70 | "This should be None for processor-independent job types.")) | ||
87 | 71 | |||
88 | 72 | virtualized = Bool( | ||
89 | 73 | title=_('Virtualized'), required=False, readonly=True, | ||
90 | 74 | description=_( | ||
91 | 75 | "The virtualization setting required by this build farm job. " | ||
92 | 76 | "This should be None for job types that do not care whether " | ||
93 | 77 | "they run virtualized.")) | ||
94 | 78 | |||
95 | 79 | date_created = Datetime( | ||
96 | 80 | title=_("Date created"), required=True, readonly=True, | ||
97 | 81 | description=_("The timestamp when the build farm job was created.")) | ||
98 | 82 | |||
99 | 83 | date_started = Datetime( | ||
100 | 84 | title=_("Date started"), required=False, readonly=True, | ||
101 | 85 | description=_("The timestamp when the build farm job was started.")) | ||
102 | 86 | |||
103 | 87 | date_finished = Datetime( | ||
104 | 88 | title=_("Date finished"), required=False, readonly=True, | ||
105 | 89 | description=_("The timestamp when the build farm job was finished.")) | ||
106 | 90 | |||
107 | 91 | date_first_dispatched = Datetime( | ||
108 | 92 | title=_("Date finished"), required=False, readonly=True, | ||
109 | 93 | description=_("The timestamp when the build farm job was dispatched.")) | ||
110 | 94 | |||
111 | 95 | builder = Reference( | ||
112 | 96 | title=_("Builder"), schema=IBuilder, required=False, readonly=True, | ||
113 | 97 | description=_("The builder assigned to this job.")) | ||
114 | 98 | |||
115 | 99 | status = Choice( | ||
116 | 100 | title=_('Status'), required=True, | ||
117 | 101 | # Really BuildStatus, patched in | ||
118 | 102 | # _schema_circular_imports.py | ||
119 | 103 | vocabulary=DBEnumeratedType, | ||
120 | 104 | description=_("The current status of the job.")) | ||
121 | 105 | |||
122 | 106 | log = Reference( | ||
123 | 107 | schema=ILibraryFileAlias, required=False, | ||
124 | 108 | title=_( | ||
125 | 109 | "The LibraryFileAlias containing the entire log for this job.")) | ||
126 | 110 | |||
127 | 111 | job_type = Choice( | ||
128 | 112 | title=_("Job type"), required=True, readonly=True, | ||
129 | 113 | vocabulary=BuildFarmJobType, | ||
130 | 114 | description=_("The specific type of job.")) | ||
131 | 115 | |||
132 | 116 | # XXX 2010-04-21 michael.nelson bug=567922. This property | ||
133 | 117 | # can be removed once all *Build classes use the concrete | ||
134 | 118 | # BuildFarmJob. | ||
135 | 119 | has_concrete_build_farm_job = Bool( | ||
136 | 120 | title=_('Has concrete build farm job'), required=False, | ||
137 | 121 | readonly=True, description=_( | ||
138 | 122 | 'Whether this instance is or has a concrete build farm job.')) | ||
139 | 123 | |||
140 | 59 | def score(): | 124 | def score(): |
141 | 60 | """Calculate a job score appropriate for the job type in question.""" | 125 | """Calculate a job score appropriate for the job type in question.""" |
142 | 61 | 126 | ||
143 | @@ -77,18 +142,6 @@ | |||
144 | 77 | def jobAborted(): | 142 | def jobAborted(): |
145 | 78 | """'Job aborted' life cycle event, handle as appropriate.""" | 143 | """'Job aborted' life cycle event, handle as appropriate.""" |
146 | 79 | 144 | ||
147 | 80 | processor = Reference( | ||
148 | 81 | IProcessor, title=_("Processor"), | ||
149 | 82 | description=_( | ||
150 | 83 | "The Processor required by this build farm job. " | ||
151 | 84 | "For processor-independent job types please return None.")) | ||
152 | 85 | |||
153 | 86 | virtualized = Attribute( | ||
154 | 87 | _( | ||
155 | 88 | "The virtualization setting required by this build farm job. " | ||
156 | 89 | "For job types that do not care about virtualization please " | ||
157 | 90 | "return None.")) | ||
158 | 91 | |||
159 | 92 | 145 | ||
160 | 93 | class IBuildFarmJobDerived(Interface): | 146 | class IBuildFarmJobDerived(Interface): |
161 | 94 | """Common functionality required by classes delegating IBuildFarmJob. | 147 | """Common functionality required by classes delegating IBuildFarmJob. |
162 | @@ -151,3 +204,18 @@ | |||
163 | 151 | 204 | ||
164 | 152 | def cleanUp(): | 205 | def cleanUp(): |
165 | 153 | """Job's finished. Delete its supporting data.""" | 206 | """Job's finished. Delete its supporting data.""" |
166 | 207 | |||
167 | 208 | |||
168 | 209 | class IBuildFarmJobSource(Interface): | ||
169 | 210 | """A utility of BuildFarmJob used to create _things_.""" | ||
170 | 211 | |||
171 | 212 | def new(job_type, status=None, processor=None, | ||
172 | 213 | virtualized=None): | ||
173 | 214 | """Create a new `IBuildFarmJob`. | ||
174 | 215 | |||
175 | 216 | :param job_type: A `BuildFarmJobType` item. | ||
176 | 217 | :param status: A `BuildStatus` item, defaulting to PENDING. | ||
177 | 218 | :param processor: An optional processor for this job. | ||
178 | 219 | :param virtualized: An optional boolean indicating whether | ||
179 | 220 | this job should be run virtualized. | ||
180 | 221 | """ | ||
181 | 154 | 222 | ||
182 | === modified file 'lib/lp/buildmaster/model/buildfarmjob.py' | |||
183 | --- lib/lp/buildmaster/model/buildfarmjob.py 2010-04-28 08:24:54 +0000 | |||
184 | +++ lib/lp/buildmaster/model/buildfarmjob.py 2010-05-05 15:46:51 +0000 | |||
185 | @@ -12,23 +12,93 @@ | |||
186 | 12 | 12 | ||
187 | 13 | from lazr.delegates import delegates | 13 | from lazr.delegates import delegates |
188 | 14 | 14 | ||
189 | 15 | import hashlib | ||
190 | 16 | import pytz | ||
191 | 17 | |||
192 | 18 | from storm.info import get_obj_info | ||
193 | 19 | from storm.locals import Bool, DateTime, Int, Reference, Storm | ||
194 | 20 | from storm.store import Store | ||
195 | 21 | |||
196 | 15 | from zope.component import getUtility | 22 | from zope.component import getUtility |
198 | 16 | from zope.interface import implements | 23 | from zope.interface import classProvides, implements |
199 | 17 | from zope.security.proxy import removeSecurityProxy | 24 | from zope.security.proxy import removeSecurityProxy |
200 | 18 | 25 | ||
203 | 19 | from storm.store import Store | 26 | from canonical.database.constants import UTC_NOW |
204 | 20 | 27 | from canonical.database.enumcol import DBEnum | |
205 | 28 | from canonical.launchpad.interfaces.lpstorm import IMasterStore | ||
206 | 21 | from canonical.launchpad.webapp.interfaces import ( | 29 | from canonical.launchpad.webapp.interfaces import ( |
207 | 22 | DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE) | 30 | DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE) |
208 | 23 | 31 | ||
209 | 32 | from lp.buildmaster.interfaces.buildbase import BuildStatus | ||
210 | 24 | from lp.buildmaster.interfaces.buildfarmjob import ( | 33 | from lp.buildmaster.interfaces.buildfarmjob import ( |
212 | 25 | IBuildFarmJob, IBuildFarmJobDerived) | 34 | BuildFarmJobType, IBuildFarmJob, IBuildFarmJobDerived, |
213 | 35 | IBuildFarmJobSource) | ||
214 | 26 | from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet | 36 | from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet |
215 | 27 | 37 | ||
216 | 28 | 38 | ||
218 | 29 | class BuildFarmJob: | 39 | class BuildFarmJob(Storm): |
219 | 30 | """A base implementation for `IBuildFarmJob` classes.""" | 40 | """A base implementation for `IBuildFarmJob` classes.""" |
220 | 41 | |||
221 | 42 | __storm_table__ = 'BuildFarmJob' | ||
222 | 43 | |||
223 | 31 | implements(IBuildFarmJob) | 44 | implements(IBuildFarmJob) |
224 | 45 | classProvides(IBuildFarmJobSource) | ||
225 | 46 | |||
226 | 47 | id = Int(primary=True) | ||
227 | 48 | |||
228 | 49 | processor_id = Int(name='processor', allow_none=True) | ||
229 | 50 | processor = Reference(processor_id, 'Processor.id') | ||
230 | 51 | |||
231 | 52 | virtualized = Bool() | ||
232 | 53 | |||
233 | 54 | date_created = DateTime( | ||
234 | 55 | name='date_created', allow_none=False, tzinfo=pytz.UTC) | ||
235 | 56 | |||
236 | 57 | date_started = DateTime( | ||
237 | 58 | name='date_started', allow_none=True, tzinfo=pytz.UTC) | ||
238 | 59 | |||
239 | 60 | date_finished = DateTime( | ||
240 | 61 | name='date_finished', allow_none=True, tzinfo=pytz.UTC) | ||
241 | 62 | |||
242 | 63 | date_first_dispatched = DateTime( | ||
243 | 64 | name='date_first_dispatched', allow_none=True, tzinfo=pytz.UTC) | ||
244 | 65 | |||
245 | 66 | builder_id = Int(name='builder', allow_none=True) | ||
246 | 67 | builder = Reference(builder_id, 'Builder.id') | ||
247 | 68 | |||
248 | 69 | status = DBEnum(name='status', allow_none=False, enum=BuildStatus) | ||
249 | 70 | |||
250 | 71 | log_id = Int(name='log', allow_none=True) | ||
251 | 72 | log = Reference(log_id, 'LibraryFileAlias.id') | ||
252 | 73 | |||
253 | 74 | job_type = DBEnum( | ||
254 | 75 | name='job_type', allow_none=False, enum=BuildFarmJobType) | ||
255 | 76 | |||
256 | 77 | def __init__(self, job_type, status=BuildStatus.NEEDSBUILD, | ||
257 | 78 | processor=None, virtualized=None): | ||
258 | 79 | self.job_type, self.status, self.process, self.virtualized = ( | ||
259 | 80 | job_type, | ||
260 | 81 | status, | ||
261 | 82 | processor, | ||
262 | 83 | virtualized, | ||
263 | 84 | ) | ||
264 | 85 | |||
265 | 86 | @classmethod | ||
266 | 87 | def new(cls, job_type, status=BuildStatus.NEEDSBUILD, processor=None, | ||
267 | 88 | virtualized=None): | ||
268 | 89 | """See `IBuildFarmJobSource`.""" | ||
269 | 90 | build_farm_job = BuildFarmJob( | ||
270 | 91 | job_type, status, processor, virtualized) | ||
271 | 92 | |||
272 | 93 | store = IMasterStore(BuildFarmJob) | ||
273 | 94 | store.add(build_farm_job) | ||
274 | 95 | return build_farm_job | ||
275 | 96 | |||
276 | 97 | @property | ||
277 | 98 | def has_concrete_build_farm_job(self): | ||
278 | 99 | """See `IBuildFarmJob`.""" | ||
279 | 100 | # Check if the object has been added to the store. | ||
280 | 101 | return get_obj_info(self).get('store') is not None | ||
281 | 32 | 102 | ||
282 | 33 | def score(self): | 103 | def score(self): |
283 | 34 | """See `IBuildFarmJob`.""" | 104 | """See `IBuildFarmJob`.""" |
284 | @@ -48,25 +118,34 @@ | |||
285 | 48 | 118 | ||
286 | 49 | def jobStarted(self): | 119 | def jobStarted(self): |
287 | 50 | """See `IBuildFarmJob`.""" | 120 | """See `IBuildFarmJob`.""" |
289 | 51 | pass | 121 | if not self.has_concrete_build_farm_job: |
290 | 122 | return | ||
291 | 123 | self.status = BuildStatus.BUILDING | ||
292 | 124 | # The build started, set the start time if not set already. | ||
293 | 125 | self.date_started = UTC_NOW | ||
294 | 126 | if self.date_first_dispatched is None: | ||
295 | 127 | self.date_first_dispatched = UTC_NOW | ||
296 | 52 | 128 | ||
297 | 53 | def jobReset(self): | 129 | def jobReset(self): |
298 | 54 | """See `IBuildFarmJob`.""" | 130 | """See `IBuildFarmJob`.""" |
314 | 55 | pass | 131 | if not self.has_concrete_build_farm_job: |
315 | 56 | 132 | return | |
316 | 57 | def jobAborted(self): | 133 | self.status = BuildStatus.NEEDSBUILD |
317 | 58 | """See `IBuildFarmJob`.""" | 134 | self.date_started = None |
318 | 59 | pass | 135 | |
319 | 60 | 136 | # The implementation of aborting a job is the same as resetting | |
320 | 61 | @property | 137 | # a job. |
321 | 62 | def processor(self): | 138 | jobAborted = jobReset |
322 | 63 | """See `IBuildFarmJob`.""" | 139 | |
323 | 64 | return None | 140 | @staticmethod |
324 | 65 | 141 | def addCandidateSelectionCriteria(processor, virtualized): | |
325 | 66 | @property | 142 | """See `IBuildFarmJob`.""" |
326 | 67 | def virtualized(self): | 143 | return ('') |
327 | 68 | """See `IBuildFarmJob`.""" | 144 | |
328 | 69 | return None | 145 | @staticmethod |
329 | 146 | def postprocessCandidate(job, logger): | ||
330 | 147 | """See `IBuildFarmJob`.""" | ||
331 | 148 | return True | ||
332 | 70 | 149 | ||
333 | 71 | 150 | ||
334 | 72 | class BuildFarmJobDerived: | 151 | class BuildFarmJobDerived: |
335 | @@ -92,7 +171,8 @@ | |||
336 | 92 | 171 | ||
337 | 93 | Sub-classes can override as required. | 172 | Sub-classes can override as required. |
338 | 94 | """ | 173 | """ |
340 | 95 | self._build_farm_job = BuildFarmJob() | 174 | self._build_farm_job = BuildFarmJob( |
341 | 175 | job_type=BuildFarmJobType.PACKAGEBUILD) | ||
342 | 96 | 176 | ||
343 | 97 | @classmethod | 177 | @classmethod |
344 | 98 | def getByJob(cls, job): | 178 | def getByJob(cls, job): |
345 | 99 | 179 | ||
346 | === modified file 'lib/lp/buildmaster/model/packagebuildfarmjob.py' | |||
347 | --- lib/lp/buildmaster/model/packagebuildfarmjob.py 2010-04-28 08:24:54 +0000 | |||
348 | +++ lib/lp/buildmaster/model/packagebuildfarmjob.py 2010-05-05 15:46:51 +0000 | |||
349 | @@ -26,7 +26,9 @@ | |||
350 | 26 | itself a concrete class. This class (PackageBuildFarmJob) | 26 | itself a concrete class. This class (PackageBuildFarmJob) |
351 | 27 | will also be renamed PackageBuild and turned into a concrete class. | 27 | will also be renamed PackageBuild and turned into a concrete class. |
352 | 28 | """ | 28 | """ |
354 | 29 | super(PackageBuildFarmJob, self).__init__() | 29 | # Classes that initialise with a build are not yet using |
355 | 30 | # the concrete class, so we don't call the superclass' | ||
356 | 31 | # initialisation. | ||
357 | 30 | self.build = build | 32 | self.build = build |
358 | 31 | 33 | ||
359 | 32 | def getTitle(self): | 34 | def getTitle(self): |
360 | 33 | 35 | ||
361 | === added file 'lib/lp/buildmaster/tests/test_buildfarmjob.py' | |||
362 | --- lib/lp/buildmaster/tests/test_buildfarmjob.py 1970-01-01 00:00:00 +0000 | |||
363 | +++ lib/lp/buildmaster/tests/test_buildfarmjob.py 2010-05-05 15:46:51 +0000 | |||
364 | @@ -0,0 +1,120 @@ | |||
365 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
366 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
367 | 3 | |||
368 | 4 | """Tests for `IBuildFarmJob`.""" | ||
369 | 5 | |||
370 | 6 | __metaclass__ = type | ||
371 | 7 | |||
372 | 8 | import unittest | ||
373 | 9 | |||
374 | 10 | from storm.store import Store | ||
375 | 11 | from zope.component import getUtility | ||
376 | 12 | |||
377 | 13 | from canonical.database.sqlbase import flush_database_updates | ||
378 | 14 | from canonical.testing.layers import DatabaseFunctionalLayer | ||
379 | 15 | |||
380 | 16 | from lp.buildmaster.interfaces.buildbase import BuildStatus | ||
381 | 17 | from lp.buildmaster.interfaces.buildfarmjob import ( | ||
382 | 18 | BuildFarmJobType, IBuildFarmJob, IBuildFarmJobSource) | ||
383 | 19 | from lp.buildmaster.model.buildfarmjob import BuildFarmJob | ||
384 | 20 | from lp.testing import TestCaseWithFactory | ||
385 | 21 | |||
386 | 22 | |||
387 | 23 | class TestBuildFarmJob(TestCaseWithFactory): | ||
388 | 24 | """Tests for the build farm job object.""" | ||
389 | 25 | |||
390 | 26 | layer = DatabaseFunctionalLayer | ||
391 | 27 | |||
392 | 28 | def setUp(self): | ||
393 | 29 | """Create a build farm job with which to test.""" | ||
394 | 30 | super(TestBuildFarmJob, self).setUp() | ||
395 | 31 | self.build_farm_job = self.makeBuildFarmJob() | ||
396 | 32 | |||
397 | 33 | def makeBuildFarmJob(self): | ||
398 | 34 | return getUtility(IBuildFarmJobSource).new( | ||
399 | 35 | job_type=BuildFarmJobType.PACKAGEBUILD) | ||
400 | 36 | |||
401 | 37 | def test_has_concrete_build_farm_job(self): | ||
402 | 38 | # This temporary property returns true if the instance | ||
403 | 39 | # corresponds to a concrete database record, even if | ||
404 | 40 | # db updates have not yet been flushed, and false | ||
405 | 41 | # otherwise. | ||
406 | 42 | concrete_build_farm_job = self.makeBuildFarmJob() | ||
407 | 43 | self.failUnless(concrete_build_farm_job.has_concrete_build_farm_job) | ||
408 | 44 | |||
409 | 45 | mem_build_farm_job = BuildFarmJob( | ||
410 | 46 | job_type=BuildFarmJobType.PACKAGEBUILD) | ||
411 | 47 | self.failIf(mem_build_farm_job.has_concrete_build_farm_job) | ||
412 | 48 | |||
413 | 49 | def test_providesInterface(self): | ||
414 | 50 | # BuildFarmJob provides IBuildFarmJob | ||
415 | 51 | self.assertProvides(self.build_farm_job, IBuildFarmJob) | ||
416 | 52 | |||
417 | 53 | def test_saves_record(self): | ||
418 | 54 | # A build farm job can be stored in the database. | ||
419 | 55 | flush_database_updates() | ||
420 | 56 | store = Store.of(self.build_farm_job) | ||
421 | 57 | retrieved_job = store.find( | ||
422 | 58 | BuildFarmJob, | ||
423 | 59 | BuildFarmJob.id == self.build_farm_job.id).one() | ||
424 | 60 | self.assertEqual(self.build_farm_job, retrieved_job) | ||
425 | 61 | |||
426 | 62 | def test_default_values(self): | ||
427 | 63 | # A build farm job defaults to the NEEDSBUILD status. | ||
428 | 64 | # We flush the database updates to ensure sql defaults | ||
429 | 65 | # are set for various attributes. | ||
430 | 66 | flush_database_updates() | ||
431 | 67 | self.assertEqual( | ||
432 | 68 | BuildStatus.NEEDSBUILD, self.build_farm_job.status) | ||
433 | 69 | # The date_created is set automatically. | ||
434 | 70 | self.assertTrue(self.build_farm_job.date_created is not None) | ||
435 | 71 | # The job type is required to create a build farm job. | ||
436 | 72 | self.assertEqual( | ||
437 | 73 | BuildFarmJobType.PACKAGEBUILD, self.build_farm_job.job_type) | ||
438 | 74 | # Other attributes are unset by default. | ||
439 | 75 | self.assertEqual(None, self.build_farm_job.processor) | ||
440 | 76 | self.assertEqual(None, self.build_farm_job.virtualized) | ||
441 | 77 | self.assertEqual(None, self.build_farm_job.date_started) | ||
442 | 78 | self.assertEqual(None, self.build_farm_job.date_finished) | ||
443 | 79 | self.assertEqual(None, self.build_farm_job.date_first_dispatched) | ||
444 | 80 | self.assertEqual(None, self.build_farm_job.builder) | ||
445 | 81 | self.assertEqual(None, self.build_farm_job.log) | ||
446 | 82 | |||
447 | 83 | def test_unimplemented_methods(self): | ||
448 | 84 | # A build farm job leaves the implementation of various | ||
449 | 85 | # methods for derived classes. | ||
450 | 86 | self.assertRaises(NotImplementedError, self.build_farm_job.score) | ||
451 | 87 | self.assertRaises(NotImplementedError, self.build_farm_job.getName) | ||
452 | 88 | self.assertRaises(NotImplementedError, self.build_farm_job.getTitle) | ||
453 | 89 | |||
454 | 90 | def test_jobStarted(self): | ||
455 | 91 | # Starting a job sets the date_started and status, as well as | ||
456 | 92 | # the date first dispatched, if it is the first dispatch of | ||
457 | 93 | # this job. | ||
458 | 94 | self.build_farm_job.jobStarted() | ||
459 | 95 | self.assertTrue(self.build_farm_job.date_first_dispatched is not None) | ||
460 | 96 | self.assertTrue(self.build_farm_job.date_started is not None) | ||
461 | 97 | self.assertEqual( | ||
462 | 98 | BuildStatus.BUILDING, self.build_farm_job.status) | ||
463 | 99 | |||
464 | 100 | def test_jobReset(self): | ||
465 | 101 | # Resetting a job sets its status back to NEEDSBUILD and unsets | ||
466 | 102 | # the date_started. | ||
467 | 103 | self.build_farm_job.jobStarted() | ||
468 | 104 | self.build_farm_job.jobReset() | ||
469 | 105 | self.failUnlessEqual( | ||
470 | 106 | BuildStatus.NEEDSBUILD, self.build_farm_job.status) | ||
471 | 107 | self.failUnless(self.build_farm_job.date_started is None) | ||
472 | 108 | |||
473 | 109 | def test_jobAborted(self): | ||
474 | 110 | # Aborting a job sets its status back to NEEDSBUILD and unsets | ||
475 | 111 | # the date_started. | ||
476 | 112 | self.build_farm_job.jobStarted() | ||
477 | 113 | self.build_farm_job.jobAborted() | ||
478 | 114 | self.failUnlessEqual( | ||
479 | 115 | BuildStatus.NEEDSBUILD, self.build_farm_job.status) | ||
480 | 116 | self.failUnless(self.build_farm_job.date_started is None) | ||
481 | 117 | |||
482 | 118 | |||
483 | 119 | def test_suite(): | ||
484 | 120 | return unittest.TestLoader().loadTestsFromName(__name__) |
This branch is part of a pipeline for
https:/ /blueprints. edge.launchpad. net/soyuz/ +spec/build- generalisation /dev.launchpad. net/LEP/ GeneralBuildHis tories
https:/
In particular, this branch turns BuildFarmJob into a concrete class, while at the same time, continuing support temporarily for classes that still expect this to be an in-memory object.
It is dependent on the pending schema patch in a previous branch.
To test
=======
First update the test db schema (required as the db patch still needs to be updated to remove the old build table): ftest_template -f database/ schema/ pending/ michaeln- build-generalis ation.sql schema/ security. py -d launchpad_ ftest_template
psql launchpad_
bin/py database/
And then: agerecipebuild -t test_translatio ntemplatesbuild job
bin/test -vvt test_buildfarmjob -t doc/build.txt -t test_buildqueue -t test_sourcepack
The next stage will be a similar conversion for PackageBuildFar mJob, followed by switching the BinaryPackageBuild from the old build table to the new binarypackagebuild table.