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 | IProcessor, title=_("Processor"), required=False, readonly=True, |
6 | description=_( |
7 | "The Processor required by this build farm job. " |
8 | - "For processor-independent job types please return None.")) |
9 | + "This should be None for processor-independent job types.")) |
10 | |
11 | virtualized = Bool( |
12 | title=_('Virtualized'), required=False, readonly=True, |
13 | description=_( |
14 | "The virtualization setting required by this build farm job. " |
15 | - "For job types that do not care about virtualization please " |
16 | - "return None.")) |
17 | + "This should be None for job types that do not care whether " |
18 | + "they run virtualized.")) |
19 | |
20 | date_created = Datetime( |
21 | title=_("Date created"), required=True, readonly=True, |
22 | @@ -90,7 +90,7 @@ |
23 | |
24 | date_first_dispatched = Datetime( |
25 | title=_("Date finished"), required=False, readonly=True, |
26 | - description=_("The timestamp when the build farm job was finished.")) |
27 | + description=_("The timestamp when the build farm job was dispatched.")) |
28 | |
29 | builder = Reference( |
30 | title=_("Builder"), schema=IBuilder, required=False, readonly=True, |
31 | @@ -98,7 +98,7 @@ |
32 | |
33 | status = Choice( |
34 | title=_('Status'), required=True, |
35 | - # Really PackagePublishingPocket, patched in |
36 | + # Really BuildStatus, patched in |
37 | # _schema_circular_imports.py |
38 | vocabulary=DBEnumeratedType, |
39 | description=_("The current status of the job.")) |
40 | |
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 | job_type = DBEnum( |
46 | name='job_type', allow_none=False, enum=BuildFarmJobType) |
47 | |
48 | - def __init__(self, job_type, status=None, processor=None, |
49 | - virtualized=None): |
50 | - if status is None: |
51 | - status = BuildStatus.NEEDSBUILD |
52 | + def __init__(self, job_type, status=BuildStatus.NEEDSBUILD, |
53 | + processor=None, virtualized=None): |
54 | self.job_type, self.status, self.process, self.virtualized = ( |
55 | job_type, |
56 | status, |
57 | @@ -83,7 +81,7 @@ |
58 | ) |
59 | |
60 | @classmethod |
61 | - def new(cls, job_type, status=None, processor=None, |
62 | + def new(cls, job_type, status=BuildStatus.NEEDSBUILD, processor=None, |
63 | virtualized=None): |
64 | """See `IBuildFarmJobSource`.""" |
65 | 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 | from lp.bugs.interfaces.bugtracker import IBugTracker |
6 | from lp.bugs.interfaces.bugwatch import IBugWatch |
7 | from lp.buildmaster.interfaces.buildbase import BuildStatus |
8 | +from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob |
9 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild |
10 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
11 | from lp.blueprints.interfaces.specification import ISpecification |
12 | @@ -271,6 +272,10 @@ |
13 | patch_plain_parameter_type( |
14 | IArchive, 'deletePackagesetUploader', 'packageset', IPackageset) |
15 | |
16 | + |
17 | +# IBuildFarmJob |
18 | +IBuildFarmJob['status'].vocabulary = BuildStatus |
19 | + |
20 | # IDistribution |
21 | IDistribution['series'].value_type.schema = IDistroSeries |
22 | patch_reference_property( |
23 | |
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 | permission="zope.Public"/> |
29 | |
30 | |
31 | + <!-- BuildFarmJob --> |
32 | + <class |
33 | + class="lp.buildmaster.model.buildfarmjob.BuildFarmJob"> |
34 | + <allow |
35 | + interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob" /> |
36 | + </class> |
37 | + <securedutility |
38 | + component="lp.buildmaster.model.buildfarmjob.BuildFarmJob" |
39 | + provides="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobSource"> |
40 | + |
41 | + <allow |
42 | + interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobSource" /> |
43 | + </securedutility> |
44 | + |
45 | + |
46 | <!-- BuildQueue --> |
47 | <class |
48 | class="lp.buildmaster.model.buildqueue.BuildQueue"> |
49 | |
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 | |
55 | __all__ = [ |
56 | 'IBuildFarmJob', |
57 | + 'IBuildFarmJobSource', |
58 | 'IBuildFarmJobDerived', |
59 | 'BuildFarmJobType', |
60 | ] |
61 | |
62 | from zope.interface import Interface, Attribute |
63 | - |
64 | -from canonical.launchpad import _ |
65 | +from zope.schema import Bool, Choice, Datetime |
66 | from lazr.enum import DBEnumeratedType, DBItem |
67 | from lazr.restful.fields import Reference |
68 | + |
69 | +from canonical.launchpad import _ |
70 | +from canonical.launchpad.interfaces.librarian import ILibraryFileAlias |
71 | + |
72 | +from lp.buildmaster.interfaces.builder import IBuilder |
73 | from lp.soyuz.interfaces.processor import IProcessor |
74 | |
75 | |
76 | @@ -56,6 +61,66 @@ |
77 | class IBuildFarmJob(Interface): |
78 | """Operations that jobs for the build farm must implement.""" |
79 | |
80 | + id = Attribute('The build farm job ID.') |
81 | + |
82 | + processor = Reference( |
83 | + IProcessor, title=_("Processor"), required=False, readonly=True, |
84 | + description=_( |
85 | + "The Processor required by this build farm job. " |
86 | + "This should be None for processor-independent job types.")) |
87 | + |
88 | + virtualized = Bool( |
89 | + title=_('Virtualized'), required=False, readonly=True, |
90 | + description=_( |
91 | + "The virtualization setting required by this build farm job. " |
92 | + "This should be None for job types that do not care whether " |
93 | + "they run virtualized.")) |
94 | + |
95 | + date_created = Datetime( |
96 | + title=_("Date created"), required=True, readonly=True, |
97 | + description=_("The timestamp when the build farm job was created.")) |
98 | + |
99 | + date_started = Datetime( |
100 | + title=_("Date started"), required=False, readonly=True, |
101 | + description=_("The timestamp when the build farm job was started.")) |
102 | + |
103 | + date_finished = Datetime( |
104 | + title=_("Date finished"), required=False, readonly=True, |
105 | + description=_("The timestamp when the build farm job was finished.")) |
106 | + |
107 | + date_first_dispatched = Datetime( |
108 | + title=_("Date finished"), required=False, readonly=True, |
109 | + description=_("The timestamp when the build farm job was dispatched.")) |
110 | + |
111 | + builder = Reference( |
112 | + title=_("Builder"), schema=IBuilder, required=False, readonly=True, |
113 | + description=_("The builder assigned to this job.")) |
114 | + |
115 | + status = Choice( |
116 | + title=_('Status'), required=True, |
117 | + # Really BuildStatus, patched in |
118 | + # _schema_circular_imports.py |
119 | + vocabulary=DBEnumeratedType, |
120 | + description=_("The current status of the job.")) |
121 | + |
122 | + log = Reference( |
123 | + schema=ILibraryFileAlias, required=False, |
124 | + title=_( |
125 | + "The LibraryFileAlias containing the entire log for this job.")) |
126 | + |
127 | + job_type = Choice( |
128 | + title=_("Job type"), required=True, readonly=True, |
129 | + vocabulary=BuildFarmJobType, |
130 | + description=_("The specific type of job.")) |
131 | + |
132 | + # XXX 2010-04-21 michael.nelson bug=567922. This property |
133 | + # can be removed once all *Build classes use the concrete |
134 | + # BuildFarmJob. |
135 | + has_concrete_build_farm_job = Bool( |
136 | + title=_('Has concrete build farm job'), required=False, |
137 | + readonly=True, description=_( |
138 | + 'Whether this instance is or has a concrete build farm job.')) |
139 | + |
140 | def score(): |
141 | """Calculate a job score appropriate for the job type in question.""" |
142 | |
143 | @@ -77,18 +142,6 @@ |
144 | def jobAborted(): |
145 | """'Job aborted' life cycle event, handle as appropriate.""" |
146 | |
147 | - processor = Reference( |
148 | - IProcessor, title=_("Processor"), |
149 | - description=_( |
150 | - "The Processor required by this build farm job. " |
151 | - "For processor-independent job types please return None.")) |
152 | - |
153 | - virtualized = Attribute( |
154 | - _( |
155 | - "The virtualization setting required by this build farm job. " |
156 | - "For job types that do not care about virtualization please " |
157 | - "return None.")) |
158 | - |
159 | |
160 | class IBuildFarmJobDerived(Interface): |
161 | """Common functionality required by classes delegating IBuildFarmJob. |
162 | @@ -151,3 +204,18 @@ |
163 | |
164 | def cleanUp(): |
165 | """Job's finished. Delete its supporting data.""" |
166 | + |
167 | + |
168 | +class IBuildFarmJobSource(Interface): |
169 | + """A utility of BuildFarmJob used to create _things_.""" |
170 | + |
171 | + def new(job_type, status=None, processor=None, |
172 | + virtualized=None): |
173 | + """Create a new `IBuildFarmJob`. |
174 | + |
175 | + :param job_type: A `BuildFarmJobType` item. |
176 | + :param status: A `BuildStatus` item, defaulting to PENDING. |
177 | + :param processor: An optional processor for this job. |
178 | + :param virtualized: An optional boolean indicating whether |
179 | + this job should be run virtualized. |
180 | + """ |
181 | |
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 | |
187 | from lazr.delegates import delegates |
188 | |
189 | +import hashlib |
190 | +import pytz |
191 | + |
192 | +from storm.info import get_obj_info |
193 | +from storm.locals import Bool, DateTime, Int, Reference, Storm |
194 | +from storm.store import Store |
195 | + |
196 | from zope.component import getUtility |
197 | -from zope.interface import implements |
198 | +from zope.interface import classProvides, implements |
199 | from zope.security.proxy import removeSecurityProxy |
200 | |
201 | -from storm.store import Store |
202 | - |
203 | +from canonical.database.constants import UTC_NOW |
204 | +from canonical.database.enumcol import DBEnum |
205 | +from canonical.launchpad.interfaces.lpstorm import IMasterStore |
206 | from canonical.launchpad.webapp.interfaces import ( |
207 | DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE) |
208 | |
209 | +from lp.buildmaster.interfaces.buildbase import BuildStatus |
210 | from lp.buildmaster.interfaces.buildfarmjob import ( |
211 | - IBuildFarmJob, IBuildFarmJobDerived) |
212 | + BuildFarmJobType, IBuildFarmJob, IBuildFarmJobDerived, |
213 | + IBuildFarmJobSource) |
214 | from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet |
215 | |
216 | |
217 | -class BuildFarmJob: |
218 | +class BuildFarmJob(Storm): |
219 | """A base implementation for `IBuildFarmJob` classes.""" |
220 | + |
221 | + __storm_table__ = 'BuildFarmJob' |
222 | + |
223 | implements(IBuildFarmJob) |
224 | + classProvides(IBuildFarmJobSource) |
225 | + |
226 | + id = Int(primary=True) |
227 | + |
228 | + processor_id = Int(name='processor', allow_none=True) |
229 | + processor = Reference(processor_id, 'Processor.id') |
230 | + |
231 | + virtualized = Bool() |
232 | + |
233 | + date_created = DateTime( |
234 | + name='date_created', allow_none=False, tzinfo=pytz.UTC) |
235 | + |
236 | + date_started = DateTime( |
237 | + name='date_started', allow_none=True, tzinfo=pytz.UTC) |
238 | + |
239 | + date_finished = DateTime( |
240 | + name='date_finished', allow_none=True, tzinfo=pytz.UTC) |
241 | + |
242 | + date_first_dispatched = DateTime( |
243 | + name='date_first_dispatched', allow_none=True, tzinfo=pytz.UTC) |
244 | + |
245 | + builder_id = Int(name='builder', allow_none=True) |
246 | + builder = Reference(builder_id, 'Builder.id') |
247 | + |
248 | + status = DBEnum(name='status', allow_none=False, enum=BuildStatus) |
249 | + |
250 | + log_id = Int(name='log', allow_none=True) |
251 | + log = Reference(log_id, 'LibraryFileAlias.id') |
252 | + |
253 | + job_type = DBEnum( |
254 | + name='job_type', allow_none=False, enum=BuildFarmJobType) |
255 | + |
256 | + def __init__(self, job_type, status=BuildStatus.NEEDSBUILD, |
257 | + processor=None, virtualized=None): |
258 | + self.job_type, self.status, self.process, self.virtualized = ( |
259 | + job_type, |
260 | + status, |
261 | + processor, |
262 | + virtualized, |
263 | + ) |
264 | + |
265 | + @classmethod |
266 | + def new(cls, job_type, status=BuildStatus.NEEDSBUILD, processor=None, |
267 | + virtualized=None): |
268 | + """See `IBuildFarmJobSource`.""" |
269 | + build_farm_job = BuildFarmJob( |
270 | + job_type, status, processor, virtualized) |
271 | + |
272 | + store = IMasterStore(BuildFarmJob) |
273 | + store.add(build_farm_job) |
274 | + return build_farm_job |
275 | + |
276 | + @property |
277 | + def has_concrete_build_farm_job(self): |
278 | + """See `IBuildFarmJob`.""" |
279 | + # Check if the object has been added to the store. |
280 | + return get_obj_info(self).get('store') is not None |
281 | |
282 | def score(self): |
283 | """See `IBuildFarmJob`.""" |
284 | @@ -48,25 +118,34 @@ |
285 | |
286 | def jobStarted(self): |
287 | """See `IBuildFarmJob`.""" |
288 | - pass |
289 | + if not self.has_concrete_build_farm_job: |
290 | + return |
291 | + self.status = BuildStatus.BUILDING |
292 | + # The build started, set the start time if not set already. |
293 | + self.date_started = UTC_NOW |
294 | + if self.date_first_dispatched is None: |
295 | + self.date_first_dispatched = UTC_NOW |
296 | |
297 | def jobReset(self): |
298 | """See `IBuildFarmJob`.""" |
299 | - pass |
300 | - |
301 | - def jobAborted(self): |
302 | - """See `IBuildFarmJob`.""" |
303 | - pass |
304 | - |
305 | - @property |
306 | - def processor(self): |
307 | - """See `IBuildFarmJob`.""" |
308 | - return None |
309 | - |
310 | - @property |
311 | - def virtualized(self): |
312 | - """See `IBuildFarmJob`.""" |
313 | - return None |
314 | + if not self.has_concrete_build_farm_job: |
315 | + return |
316 | + self.status = BuildStatus.NEEDSBUILD |
317 | + self.date_started = None |
318 | + |
319 | + # The implementation of aborting a job is the same as resetting |
320 | + # a job. |
321 | + jobAborted = jobReset |
322 | + |
323 | + @staticmethod |
324 | + def addCandidateSelectionCriteria(processor, virtualized): |
325 | + """See `IBuildFarmJob`.""" |
326 | + return ('') |
327 | + |
328 | + @staticmethod |
329 | + def postprocessCandidate(job, logger): |
330 | + """See `IBuildFarmJob`.""" |
331 | + return True |
332 | |
333 | |
334 | class BuildFarmJobDerived: |
335 | @@ -92,7 +171,8 @@ |
336 | |
337 | Sub-classes can override as required. |
338 | """ |
339 | - self._build_farm_job = BuildFarmJob() |
340 | + self._build_farm_job = BuildFarmJob( |
341 | + job_type=BuildFarmJobType.PACKAGEBUILD) |
342 | |
343 | @classmethod |
344 | def getByJob(cls, job): |
345 | |
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 | itself a concrete class. This class (PackageBuildFarmJob) |
351 | will also be renamed PackageBuild and turned into a concrete class. |
352 | """ |
353 | - super(PackageBuildFarmJob, self).__init__() |
354 | + # Classes that initialise with a build are not yet using |
355 | + # the concrete class, so we don't call the superclass' |
356 | + # initialisation. |
357 | self.build = build |
358 | |
359 | def getTitle(self): |
360 | |
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 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
366 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
367 | + |
368 | +"""Tests for `IBuildFarmJob`.""" |
369 | + |
370 | +__metaclass__ = type |
371 | + |
372 | +import unittest |
373 | + |
374 | +from storm.store import Store |
375 | +from zope.component import getUtility |
376 | + |
377 | +from canonical.database.sqlbase import flush_database_updates |
378 | +from canonical.testing.layers import DatabaseFunctionalLayer |
379 | + |
380 | +from lp.buildmaster.interfaces.buildbase import BuildStatus |
381 | +from lp.buildmaster.interfaces.buildfarmjob import ( |
382 | + BuildFarmJobType, IBuildFarmJob, IBuildFarmJobSource) |
383 | +from lp.buildmaster.model.buildfarmjob import BuildFarmJob |
384 | +from lp.testing import TestCaseWithFactory |
385 | + |
386 | + |
387 | +class TestBuildFarmJob(TestCaseWithFactory): |
388 | + """Tests for the build farm job object.""" |
389 | + |
390 | + layer = DatabaseFunctionalLayer |
391 | + |
392 | + def setUp(self): |
393 | + """Create a build farm job with which to test.""" |
394 | + super(TestBuildFarmJob, self).setUp() |
395 | + self.build_farm_job = self.makeBuildFarmJob() |
396 | + |
397 | + def makeBuildFarmJob(self): |
398 | + return getUtility(IBuildFarmJobSource).new( |
399 | + job_type=BuildFarmJobType.PACKAGEBUILD) |
400 | + |
401 | + def test_has_concrete_build_farm_job(self): |
402 | + # This temporary property returns true if the instance |
403 | + # corresponds to a concrete database record, even if |
404 | + # db updates have not yet been flushed, and false |
405 | + # otherwise. |
406 | + concrete_build_farm_job = self.makeBuildFarmJob() |
407 | + self.failUnless(concrete_build_farm_job.has_concrete_build_farm_job) |
408 | + |
409 | + mem_build_farm_job = BuildFarmJob( |
410 | + job_type=BuildFarmJobType.PACKAGEBUILD) |
411 | + self.failIf(mem_build_farm_job.has_concrete_build_farm_job) |
412 | + |
413 | + def test_providesInterface(self): |
414 | + # BuildFarmJob provides IBuildFarmJob |
415 | + self.assertProvides(self.build_farm_job, IBuildFarmJob) |
416 | + |
417 | + def test_saves_record(self): |
418 | + # A build farm job can be stored in the database. |
419 | + flush_database_updates() |
420 | + store = Store.of(self.build_farm_job) |
421 | + retrieved_job = store.find( |
422 | + BuildFarmJob, |
423 | + BuildFarmJob.id == self.build_farm_job.id).one() |
424 | + self.assertEqual(self.build_farm_job, retrieved_job) |
425 | + |
426 | + def test_default_values(self): |
427 | + # A build farm job defaults to the NEEDSBUILD status. |
428 | + # We flush the database updates to ensure sql defaults |
429 | + # are set for various attributes. |
430 | + flush_database_updates() |
431 | + self.assertEqual( |
432 | + BuildStatus.NEEDSBUILD, self.build_farm_job.status) |
433 | + # The date_created is set automatically. |
434 | + self.assertTrue(self.build_farm_job.date_created is not None) |
435 | + # The job type is required to create a build farm job. |
436 | + self.assertEqual( |
437 | + BuildFarmJobType.PACKAGEBUILD, self.build_farm_job.job_type) |
438 | + # Other attributes are unset by default. |
439 | + self.assertEqual(None, self.build_farm_job.processor) |
440 | + self.assertEqual(None, self.build_farm_job.virtualized) |
441 | + self.assertEqual(None, self.build_farm_job.date_started) |
442 | + self.assertEqual(None, self.build_farm_job.date_finished) |
443 | + self.assertEqual(None, self.build_farm_job.date_first_dispatched) |
444 | + self.assertEqual(None, self.build_farm_job.builder) |
445 | + self.assertEqual(None, self.build_farm_job.log) |
446 | + |
447 | + def test_unimplemented_methods(self): |
448 | + # A build farm job leaves the implementation of various |
449 | + # methods for derived classes. |
450 | + self.assertRaises(NotImplementedError, self.build_farm_job.score) |
451 | + self.assertRaises(NotImplementedError, self.build_farm_job.getName) |
452 | + self.assertRaises(NotImplementedError, self.build_farm_job.getTitle) |
453 | + |
454 | + def test_jobStarted(self): |
455 | + # Starting a job sets the date_started and status, as well as |
456 | + # the date first dispatched, if it is the first dispatch of |
457 | + # this job. |
458 | + self.build_farm_job.jobStarted() |
459 | + self.assertTrue(self.build_farm_job.date_first_dispatched is not None) |
460 | + self.assertTrue(self.build_farm_job.date_started is not None) |
461 | + self.assertEqual( |
462 | + BuildStatus.BUILDING, self.build_farm_job.status) |
463 | + |
464 | + def test_jobReset(self): |
465 | + # Resetting a job sets its status back to NEEDSBUILD and unsets |
466 | + # the date_started. |
467 | + self.build_farm_job.jobStarted() |
468 | + self.build_farm_job.jobReset() |
469 | + self.failUnlessEqual( |
470 | + BuildStatus.NEEDSBUILD, self.build_farm_job.status) |
471 | + self.failUnless(self.build_farm_job.date_started is None) |
472 | + |
473 | + def test_jobAborted(self): |
474 | + # Aborting a job sets its status back to NEEDSBUILD and unsets |
475 | + # the date_started. |
476 | + self.build_farm_job.jobStarted() |
477 | + self.build_farm_job.jobAborted() |
478 | + self.failUnlessEqual( |
479 | + BuildStatus.NEEDSBUILD, self.build_farm_job.status) |
480 | + self.failUnless(self.build_farm_job.date_started is None) |
481 | + |
482 | + |
483 | +def test_suite(): |
484 | + 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.