Merge lp:~michael.nelson/launchpad/487009-db-generalise-ibuilder-1b into lp:launchpad/db-devel

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Nelson
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~michael.nelson/launchpad/487009-db-generalise-ibuilder-1b
Merge into: lp:launchpad/db-devel
Diff against target: 481 lines (+296/-7)
11 files modified
lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py (+38/-0)
lib/lp/buildmaster/model/buildfarmjobbehavior.py (+50/-0)
lib/lp/soyuz/configure.zcml (+7/-0)
lib/lp/soyuz/doc/builder.txt (+12/-0)
lib/lp/soyuz/doc/buildqueue.txt (+10/-0)
lib/lp/soyuz/interfaces/builder.py (+8/-2)
lib/lp/soyuz/interfaces/buildqueue.py (+5/-1)
lib/lp/soyuz/model/binarypackagebuildbehavior.py (+35/-0)
lib/lp/soyuz/model/builder.py (+55/-4)
lib/lp/soyuz/model/buildqueue.py (+7/-0)
lib/lp/soyuz/tests/test_builder.py (+69/-0)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/487009-db-generalise-ibuilder-1b
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+15477@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

= Summary =

This branch creates the infrastructure to allow IBuilder to support different behaviors (BinaryPackageBuildBehavior currently, but later SourcePackageRecipeBuildBehavior etc.).

== Proposed fix ==

Create a general interface for build farm job behaviors that can be implemented by various classes, and allow the behavior to be an attribute of the builder to which all relevant calls will be delegated.

This branch begins the extraction of soyuz-specific build behavior for the [Binary]BuildPackageJob into a BinaryPackageBuildBehavior. Subsequent branches (in a pipeline) will continue this.

I plan to re-base these to devel after the release when db-stable is merged back into devel.

== Pre-implementation notes ==

See the pre-implementation notes on bug 487009.

== Implementation details ==

== Tests ==

bin/test -vv -t doc/builder.txt -t doc/buildqueue.txt -t TestCurrentBuildBehavior.txt

== Demo and Q/A ==

We will need to Q/A this on dogfood.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/soyuz/model/builder.py
  lib/lp/soyuz/doc/builder.txt
  lib/lp/soyuz/doc/buildqueue.txt
  lib/lp/soyuz/configure.zcml
  lib/lp/soyuz/model/buildqueue.py
  lib/lp/buildmaster/model/buildfarmjobbehavior.py
  lib/lp/soyuz/interfaces/buildqueue.py
  lib/lp/soyuz/tests/test_builder.py
  lib/lp/soyuz/model/binarypackagebuildbehavior.py
  lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py
  lib/lp/soyuz/interfaces/builder.py

== Pylint notices ==

lib/lp/soyuz/model/builder.py
    23: [F0401] Unable to import 'lazr.delegates' (No module named delegates)

lib/lp/soyuz/interfaces/buildqueue.py
    18: [F0401] Unable to import 'lazr.restful.fields' (No module named restful)

--
Michael

Revision history for this message
Michael Nelson (michael.nelson) wrote :

> == Tests ==
>
> bin/test -vv -t doc/builder.txt -t doc/buildqueue.txt -t
> TestCurrentBuildBehavior.txt

Also include doc/buildd-dispatching.txt which demonstrates that build dispatching still passes with the refactoring.

Revision history for this message
Graham Binns (gmb) wrote :
Download full text (19.3 KiB)

Hi Michael,

Nice branch. Good work. I've got some stylistic nitpicks - our
guidelines gleefully discard PEP8, hurrah - and I'd like you to do a
strip-trailing-whitespace run on the files you've changed (there's not
much, but enough for me to notice).

Other than that, r=me.

> === added file 'lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py'
> --- lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 2009-12-01 10:00:36 +0000
> @@ -0,0 +1,38 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +# pylint: disable-msg=E0211,E0213
> +
> +"""Interface for build farm job behaviors."""
> +
> +__metaclass__ = type
> +
> +__all__ = [
> + 'IBuildFarmJobBehavior',
> + ]
> +
> +from zope.interface import Interface
> +
> +
> +class BuildBehaviorMismatch(Exception):
> + """
> + A general exception that can be raised when the builder's current behavior
> + does not match the expected behavior.
> + """
> +
> +
> +class IBuildFarmJobBehavior(Interface):
> +
> + def set_builder(builder):
> + """Sets the associated builder reference for this instance."""
> +
> + def log_start_build(build_queue_item, logger):
> + """Log the start of a specific build queue item.
> +
> + The form of the log message will vary depending on the type of build.
> + :param build_queue_item: A BuildQueueItem to build.
> + :param logger: A logger to be used to log diagnostic information.
> + """
> + # A number of other methods to go here that can be customised for each of
> + # the different build types (branch build, recipe build, translation
> + # build etc.)
>
> === added file 'lib/lp/buildmaster/model/buildfarmjobbehavior.py'
> --- lib/lp/buildmaster/model/buildfarmjobbehavior.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/buildmaster/model/buildfarmjobbehavior.py 2009-12-01 10:00:36 +0000
> @@ -0,0 +1,53 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +# pylint: disable-msg=E0211,E0213
> +
> +"""Base and idle BuildFarmJobBehavior classes."""
> +
> +__metaclass__ = type
> +
> +__all__ = [
> + 'BuildFarmJobBehaviorBase',
> + 'IdleBuildBehavior'
> + ]
> +
> +from zope.interface import implements
> +
> +from lp.buildmaster.interfaces.buildfarmjobbehavior import (
> + BuildBehaviorMismatch, IBuildFarmJobBehavior)
> +
> +
> +class BuildFarmJobBehaviorBase:
> + """Ensures that all behaviors inherit the same initialisation.
> +
> + All build-farm job behaviors should inherit from this.
> + """
> +
> + def __init__(self, buildfarmjob):
> + """
> + Store a reference to the job_type with which we were created.
> + """
> + self.buildfarmjob = buildfarmjob
> + self._builder = None
> +
> + def set_builder(self, builder):

This should be setBuilder(), because who needs PEP8?

> + """The builder should be set once and not changed."""
> + self._builder = builde...

review: Approve (code)
Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (21.3 KiB)

On Tue, Dec 1, 2009 at 4:30 PM, Graham Binns <email address hidden> wrote:

> Review: Approve code
>
> Hi Michael,
>
> Nice branch. Good work. I've got some stylistic nitpicks - our
> guidelines gleefully discard PEP8, hurrah - and I'd like you to do a
>

gah, I'd thought I was cleaning up old code (too much lazrjs lately).

> strip-trailing-whitespace run on the files you've changed (there's not
> much, but enough for me to notice).
>
>
Sorry - switched editors recently and forgot to setup tailing whitespace
removal. I've updated my .rc file to automatically delete trailing spaces
and fixed all the files. Thanks.

> Other than that, r=me.
>
> > === added file 'lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py'
> > --- lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 1970-01-01
> 00:00:00 +0000
> > +++ lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 2009-12-01
> 10:00:36 +0000
> > @@ -0,0 +1,38 @@
> > +# Copyright 2009 Canonical Ltd. This software is licensed under the
> > +# GNU Affero General Public License version 3 (see the file LICENSE).
> > +
> > +# pylint: disable-msg=E0211,E0213
> > +
> > +"""Interface for build farm job behaviors."""
> > +
> > +__metaclass__ = type
> > +
> > +__all__ = [
> > + 'IBuildFarmJobBehavior',
> > + ]
> > +
> > +from zope.interface import Interface
> > +
> > +
> > +class BuildBehaviorMismatch(Exception):
> > + """
> > + A general exception that can be raised when the builder's current
> behavior
> > + does not match the expected behavior.
> > + """
> > +
> > +
> > +class IBuildFarmJobBehavior(Interface):
> > +
> > + def set_builder(builder):
> > + """Sets the associated builder reference for this instance."""
> > +
> > + def log_start_build(build_queue_item, logger):
> > + """Log the start of a specific build queue item.
> > +
> > + The form of the log message will vary depending on the type of
> build.
> > + :param build_queue_item: A BuildQueueItem to build.
> > + :param logger: A logger to be used to log diagnostic
> information.
> > + """
> > + # A number of other methods to go here that can be customised for
> each of
> > + # the different build types (branch build, recipe build, translation
> > + # build etc.)
> >
> > === added file 'lib/lp/buildmaster/model/buildfarmjobbehavior.py'
> > --- lib/lp/buildmaster/model/buildfarmjobbehavior.py 1970-01-01 00:00:00
> +0000
> > +++ lib/lp/buildmaster/model/buildfarmjobbehavior.py 2009-12-01 10:00:36
> +0000
> > @@ -0,0 +1,53 @@
> > +# Copyright 2009 Canonical Ltd. This software is licensed under the
> > +# GNU Affero General Public License version 3 (see the file LICENSE).
> > +
> > +# pylint: disable-msg=E0211,E0213
> > +
> > +"""Base and idle BuildFarmJobBehavior classes."""
> > +
> > +__metaclass__ = type
> > +
> > +__all__ = [
> > + 'BuildFarmJobBehaviorBase',
> > + 'IdleBuildBehavior'
> > + ]
> > +
> > +from zope.interface import implements
> > +
> > +from lp.buildmaster.interfaces.buildfarmjobbehavior import (
> > + BuildBehaviorMismatch, IBuildFarmJobBehavior)
> > +
> > +
> > +class BuildFarmJobBehaviorBase:
> > + """Ensures that all ...

1=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py'
2--- lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 2009-11-27 13:50:29 +0000
3+++ lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 2009-12-01 16:40:02 +0000
4@@ -23,10 +23,10 @@
5
6 class IBuildFarmJobBehavior(Interface):
7
8- def set_builder(builder):
9+ def setBuilder(builder):
10 """Sets the associated builder reference for this instance."""
11
12- def log_start_build(build_queue_item, logger):
13+ def logStartBuild(build_queue_item, logger):
14 """Log the start of a specific build queue item.
15
16 The form of the log message will vary depending on the type of build.
17
18=== modified file 'lib/lp/buildmaster/model/buildfarmjobbehavior.py'
19--- lib/lp/buildmaster/model/buildfarmjobbehavior.py 2009-12-01 09:43:41 +0000
20+++ lib/lp/buildmaster/model/buildfarmjobbehavior.py 2009-12-01 16:38:56 +0000
21@@ -25,13 +25,11 @@
22 """
23
24 def __init__(self, buildfarmjob):
25- """
26- Store a reference to the job_type with which we were created.
27- """
28+ """Store a reference to the job_type with which we were created."""
29 self.buildfarmjob = buildfarmjob
30 self._builder = None
31
32- def set_builder(self, builder):
33+ def setBuilder(self, builder):
34 """The builder should be set once and not changed."""
35 self._builder = builder
36
37@@ -41,9 +39,8 @@
38 implements(IBuildFarmJobBehavior)
39
40 def __init__(self):
41- """
42- The idle behavior is special in that a buildfarmjob is not specified
43- during initialisation.
44+ """The idle behavior is special in that a buildfarmjob is not
45+ specified during initialisation.
46 """
47 super(IdleBuildBehavior, self).__init__(None)
48
49
50=== modified file 'lib/lp/soyuz/model/binarypackagebuildbehavior.py'
51--- lib/lp/soyuz/model/binarypackagebuildbehavior.py 2009-12-01 09:02:14 +0000
52+++ lib/lp/soyuz/model/binarypackagebuildbehavior.py 2009-12-01 16:39:48 +0000
53@@ -26,8 +26,8 @@
54
55 implements(IBuildFarmJobBehavior)
56
57- def log_start_build(self, build_queue_item, logger):
58- """See `IBuildFarmJobBehavior`."""
59+ def logStartBuild(self, build_queue_item, logger):
60+ """See `IBuildFarmJobBehavior`."""
61 build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
62 spr = build.sourcepackagerelease
63
64
65=== modified file 'lib/lp/soyuz/model/builder.py'
66--- lib/lp/soyuz/model/builder.py 2009-12-01 09:38:05 +0000
67+++ lib/lp/soyuz/model/builder.py 2009-12-01 16:37:20 +0000
68@@ -142,7 +142,7 @@
69 vm_host = StringCol(dbName='vm_host')
70 active = BoolCol(dbName='active', notNull=True, default=True)
71
72- def _get_current_build_behavior(self):
73+ def _getCurrentBuildBehavior(self):
74 """Return the current build behavior."""
75 if not safe_hasattr(self, '_current_build_behavior'):
76 self._current_build_behavior = None
77@@ -156,14 +156,14 @@
78 # ...we'll set it based on our current job.
79 self._current_build_behavior = (
80 currentjob.required_build_behavior)
81- self._current_build_behavior.set_builder(self)
82+ self._current_build_behavior.setBuilder(self)
83 return self._current_build_behavior
84 elif self._current_build_behavior is None:
85 # If we don't have a current job or an idle behavior
86 # already set, then we just set the idle behavior
87 # before returning.
88 self._current_build_behavior = IdleBuildBehavior()
89- return self._current_build_behavior
90+ return self._current_build_behavior
91
92 else:
93 # We did have a current non-idle build behavior set, so
94@@ -171,7 +171,7 @@
95 return self._current_build_behavior
96
97
98- def _set_current_build_behavior(self, new_behavior):
99+ def _setCurrentBuildBehavior(self, new_behavior):
100 """Set the current build behavior."""
101
102 if self.currentjob is not None:
103@@ -182,10 +182,10 @@
104 "exists.")
105 else:
106 self._current_build_behavior = new_behavior
107- self._current_build_behavior.set_builder(self)
108+ self._current_build_behavior.setBuilder(self)
109
110 current_build_behavior = property(
111- _get_current_build_behavior, _set_current_build_behavior)
112+ _getCurrentBuildBehavior, _setCurrentBuildBehavior)
113
114 def cacheFileOnSlave(self, logger, libraryfilealias):
115 """See `IBuilder`."""
116@@ -403,9 +403,9 @@
117
118 def startBuild(self, build_queue_item, logger):
119 """See IBuilder."""
120- # Set the build behavior depending on the provided build queue item.
121+ # Set the build behavior depending on the provided build queue item.
122 self.current_build_behavior = build_queue_item.required_build_behavior
123- self.log_start_build(build_queue_item, logger)
124+ self.logStartBuild(build_queue_item, logger)
125
126 # Make sure the request is valid; an exception is raised if it's not.
127 self._verifyBuildRequest(build_queue_item, logger)
128
129=== modified file 'lib/lp/soyuz/tests/test_builder.py'
130--- lib/lp/soyuz/tests/test_builder.py 2009-12-01 09:43:41 +0000
131+++ lib/lp/soyuz/tests/test_builder.py 2009-12-01 16:39:29 +0000
132@@ -221,8 +221,8 @@
133
134
135 class TestCurrentBuildBehavior(TestCaseWithFactory):
136- """
137- This test ensures the get/set behavior of Builder.current_build_behavior.
138+ """This test ensures the get/set behavior of IBuilder's
139+ current_build_behavior property.
140 """
141
142 layer = LaunchpadZopelessLayer
143@@ -231,7 +231,7 @@
144 """Create a new builder ready for testing."""
145 super(TestCurrentBuildBehavior, self).setUp()
146 self.builder = self.factory.makeBuilder(name='builder')
147-
148+
149 # Have a publisher and a ppa handy for some of the tests below.
150 self.publisher = SoyuzTestPublisher()
151 self.publisher.prepareBreezyAutotest()
152@@ -244,8 +244,7 @@
153 self.buildfarmjob = self.build.buildqueue_record.specific_job
154
155 def test_idle_behavior_when_no_current_build(self):
156- """
157- We return an idle behavior when there is no behavior specified
158+ """We return an idle behavior when there is no behavior specified
159 nor a current build.
160 """
161 self.assertIsInstance(
162@@ -254,7 +253,7 @@
163 def test_set_behavior_when_no_current_job(self):
164 """If a builder is idle then it is possible to set the behavior."""
165 self.builder.current_build_behavior = IBuildFarmJobBehavior(
166- self.buildfarmjob)
167+ self.buildfarmjob)
168
169 self.assertIsInstance(
170 self.builder.current_build_behavior, BinaryPackageBuildBehavior)
171@@ -269,9 +268,7 @@
172 self.builder.current_build_behavior, BinaryPackageBuildBehavior)
173
174 def test_set_behavior_when_current_job(self):
175- """
176- If a builder has a current job then it's behavior cannot be
177- set.
178+ """If a builder has a current job then it's behavior cannot be set.
179 """
180 self.build.buildqueue_record.builder = self.builder
181
182@@ -280,7 +277,7 @@
183 assertion_raised = False
184 try:
185 self.builder.current_build_behavior = IBuildFarmJobBehavior(
186- self.buildfarmjob)
187+ self.buildfarmjob)
188 except BuildBehaviorMismatch, e:
189 assertion_raised = True
190

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py'
2--- lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 1970-01-01 00:00:00 +0000
3+++ lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 2009-12-01 16:47:13 +0000
4@@ -0,0 +1,38 @@
5+# Copyright 2009 Canonical Ltd. This software is licensed under the
6+# GNU Affero General Public License version 3 (see the file LICENSE).
7+
8+# pylint: disable-msg=E0211,E0213
9+
10+"""Interface for build farm job behaviors."""
11+
12+__metaclass__ = type
13+
14+__all__ = [
15+ 'IBuildFarmJobBehavior',
16+ ]
17+
18+from zope.interface import Interface
19+
20+
21+class BuildBehaviorMismatch(Exception):
22+ """
23+ A general exception that can be raised when the builder's current behavior
24+ does not match the expected behavior.
25+ """
26+
27+
28+class IBuildFarmJobBehavior(Interface):
29+
30+ def setBuilder(builder):
31+ """Sets the associated builder reference for this instance."""
32+
33+ def logStartBuild(build_queue_item, logger):
34+ """Log the start of a specific build queue item.
35+
36+ The form of the log message will vary depending on the type of build.
37+ :param build_queue_item: A BuildQueueItem to build.
38+ :param logger: A logger to be used to log diagnostic information.
39+ """
40+ # A number of other methods to go here that can be customised for each of
41+ # the different build types (branch build, recipe build, translation
42+ # build etc.)
43
44=== added file 'lib/lp/buildmaster/model/buildfarmjobbehavior.py'
45--- lib/lp/buildmaster/model/buildfarmjobbehavior.py 1970-01-01 00:00:00 +0000
46+++ lib/lp/buildmaster/model/buildfarmjobbehavior.py 2009-12-01 16:47:13 +0000
47@@ -0,0 +1,50 @@
48+# Copyright 2009 Canonical Ltd. This software is licensed under the
49+# GNU Affero General Public License version 3 (see the file LICENSE).
50+
51+# pylint: disable-msg=E0211,E0213
52+
53+"""Base and idle BuildFarmJobBehavior classes."""
54+
55+__metaclass__ = type
56+
57+__all__ = [
58+ 'BuildFarmJobBehaviorBase',
59+ 'IdleBuildBehavior'
60+ ]
61+
62+from zope.interface import implements
63+
64+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
65+ BuildBehaviorMismatch, IBuildFarmJobBehavior)
66+
67+
68+class BuildFarmJobBehaviorBase:
69+ """Ensures that all behaviors inherit the same initialisation.
70+
71+ All build-farm job behaviors should inherit from this.
72+ """
73+
74+ def __init__(self, buildfarmjob):
75+ """Store a reference to the job_type with which we were created."""
76+ self.buildfarmjob = buildfarmjob
77+ self._builder = None
78+
79+ def setBuilder(self, builder):
80+ """The builder should be set once and not changed."""
81+ self._builder = builder
82+
83+
84+class IdleBuildBehavior(BuildFarmJobBehaviorBase):
85+
86+ implements(IBuildFarmJobBehavior)
87+
88+ def __init__(self):
89+ """The idle behavior is special in that a buildfarmjob is not
90+ specified during initialisation.
91+ """
92+ super(IdleBuildBehavior, self).__init__(None)
93+
94+ def logStartBuild(self, build_queue_item, logger):
95+ """See `IBuildFarmJobBehavior`."""
96+ raise BuildBehaviorMismatch(
97+ "Builder was idle when asked to log the start of a build.")
98
99=== modified file 'lib/lp/soyuz/configure.zcml'
100--- lib/lp/soyuz/configure.zcml 2009-11-16 22:06:14 +0000
101+++ lib/lp/soyuz/configure.zcml 2009-12-01 16:47:13 +0000
102@@ -892,4 +892,11 @@
103 interface="lp.soyuz.interfaces.buildpackagejob.IBuildPackageJob"/>
104 </class>
105
106+ <!-- BinaryPackageBuildBehavior -->
107+ <adapter
108+ for="lp.soyuz.interfaces.buildpackagejob.IBuildPackageJob"
109+ provides="lp.buildmaster.interfaces.buildfarmjobbehavior.IBuildFarmJobBehavior"
110+ factory="lp.soyuz.model.binarypackagebuildbehavior.BinaryPackageBuildBehavior"
111+ permission="zope.Public" />
112+
113 </configure>
114
115=== modified file 'lib/lp/soyuz/doc/builder.txt'
116--- lib/lp/soyuz/doc/builder.txt 2009-11-12 12:52:23 +0000
117+++ lib/lp/soyuz/doc/builder.txt 2009-12-01 16:47:13 +0000
118@@ -40,6 +40,18 @@
119 >>> builder.builderok = True
120 >>> builder.failnotes = None
121
122+A builder provides a current_build_behavior attribute to which all the
123+build-type specific behavior for the current build is delegated. For
124+our sample data, the behavior is specifically a behavior for dealing
125+with binary packages
126+
127+ >>> from zope.security.proxy import isinstance
128+ >>> from lp.soyuz.model.binarypackagebuildbehavior import (
129+ ... BinaryPackageBuildBehavior)
130+ >>> isinstance(
131+ ... builder.current_build_behavior, BinaryPackageBuildBehavior)
132+ True
133+
134 In case of copy archives the status string will show both the copy
135 archive owner as well as the copy archive name.
136
137
138=== modified file 'lib/lp/soyuz/doc/buildqueue.txt'
139--- lib/lp/soyuz/doc/buildqueue.txt 2009-11-13 19:34:17 +0000
140+++ lib/lp/soyuz/doc/buildqueue.txt 2009-12-01 16:47:13 +0000
141@@ -98,6 +98,16 @@
142 (True, 1000)
143
144
145+The BuildQueue item is responsible for providing the required build behavior
146+for the item.
147+
148+ >>> from zope.security.proxy import isinstance
149+ >>> from lp.soyuz.model.binarypackagebuildbehavior import (
150+ ... BinaryPackageBuildBehavior)
151+ >>> isinstance(bq.required_build_behavior, BinaryPackageBuildBehavior)
152+ True
153+
154+
155 == Dispatching and Reseting jobs ==
156
157 The sampledata contains an active job, being built by the 'bob'
158
159=== modified file 'lib/lp/soyuz/interfaces/builder.py'
160--- lib/lp/soyuz/interfaces/builder.py 2009-11-20 18:06:28 +0000
161+++ lib/lp/soyuz/interfaces/builder.py 2009-12-01 16:47:13 +0000
162@@ -19,11 +19,13 @@
163 ]
164
165 from zope.interface import Interface, Attribute
166-from zope.schema import Choice, TextLine, Text, Bool
167+from zope.schema import Bool, Choice, Field, Text, TextLine
168
169 from canonical.launchpad import _
170 from canonical.launchpad.fields import Title, Description
171 from lp.registry.interfaces.role import IHasOwner
172+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
173+ IBuildFarmJobBehavior)
174 from canonical.launchpad.validators.name import name_validator
175 from canonical.launchpad.validators.url import builder_url_validator
176
177@@ -57,7 +59,7 @@
178 """The build slave has suffered an error and cannot be used."""
179
180
181-class IBuilder(IHasOwner):
182+class IBuilder(IHasOwner, IBuildFarmJobBehavior):
183 """Build-slave information and state.
184
185 Builder instance represents a single builder slave machine within the
186@@ -138,6 +140,10 @@
187 "new jobs. "),
188 required=False)
189
190+ current_build_behavior = Field(
191+ title=u"The current behavior of the builder for the current job.",
192+ required=False)
193+
194 def cacheFileOnSlave(logger, libraryfilealias):
195 """Ask the slave to cache a librarian file to its local disk.
196
197
198=== modified file 'lib/lp/soyuz/interfaces/buildqueue.py'
199--- lib/lp/soyuz/interfaces/buildqueue.py 2009-11-20 18:06:28 +0000
200+++ lib/lp/soyuz/interfaces/buildqueue.py 2009-12-01 16:47:13 +0000
201@@ -13,7 +13,7 @@
202 ]
203
204 from zope.interface import Interface, Attribute
205-from zope.schema import Choice, Datetime, Timedelta
206+from zope.schema import Choice, Datetime, Field, Timedelta
207
208 from lazr.restful.fields import Reference
209
210@@ -51,6 +51,10 @@
211 title=_('Job type'), required=True, vocabulary=BuildFarmJobType,
212 description=_("The type of this job."))
213
214+ required_build_behavior = Field(
215+ title=_('The builder behavior required to run this job.'),
216+ required=False, readonly=True)
217+
218 estimated_duration = Timedelta(
219 title=_("Estimated Job Duration"), required=True,
220 description=_("Estimated job duration interval."))
221
222=== added file 'lib/lp/soyuz/model/binarypackagebuildbehavior.py'
223--- lib/lp/soyuz/model/binarypackagebuildbehavior.py 1970-01-01 00:00:00 +0000
224+++ lib/lp/soyuz/model/binarypackagebuildbehavior.py 2009-12-01 16:47:13 +0000
225@@ -0,0 +1,35 @@
226+# Copyright 2009 Canonical Ltd. This software is licensed under the
227+# GNU Affero General Public License version 3 (see the file LICENSE).
228+
229+# pylint: disable-msg=E0211,E0213
230+
231+"""Builder behavior for binary package builds."""
232+
233+__metaclass__ = type
234+
235+__all__ = [
236+ 'BinaryPackageBuildBehavior',
237+ ]
238+
239+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
240+ IBuildFarmJobBehavior)
241+from lp.buildmaster.model.buildfarmjobbehavior import (
242+ BuildFarmJobBehaviorBase)
243+from lp.soyuz.interfaces.build import IBuildSet
244+
245+from zope.component import getUtility
246+from zope.interface import implements
247+
248+
249+class BinaryPackageBuildBehavior(BuildFarmJobBehaviorBase):
250+ """Define the behavior of binary package builds."""
251+
252+ implements(IBuildFarmJobBehavior)
253+
254+ def logStartBuild(self, build_queue_item, logger):
255+ """See `IBuildFarmJobBehavior`."""
256+ build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
257+ spr = build.sourcepackagerelease
258+
259+ logger.info("startBuild(%s, %s, %s, %s)", self._builder.url,
260+ spr.name, spr.version, build.pocket.title)
261
262=== modified file 'lib/lp/soyuz/model/builder.py'
263--- lib/lp/soyuz/model/builder.py 2009-11-20 18:06:28 +0000
264+++ lib/lp/soyuz/model/builder.py 2009-12-01 16:47:13 +0000
265@@ -20,6 +20,8 @@
266 import urllib2
267 import xmlrpclib
268
269+from lazr.delegates import delegates
270+
271 from zope.interface import implements
272 from zope.component import getUtility
273
274@@ -31,7 +33,10 @@
275 from canonical.cachedproperty import cachedproperty
276 from canonical.config import config
277 from canonical.buildd.slave import BuilderStatus
278+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
279+ BuildBehaviorMismatch, IBuildFarmJobBehavior)
280 from lp.buildmaster.master import BuilddMaster
281+from lp.buildmaster.model.buildfarmjobbehavior import IdleBuildBehavior
282 from canonical.database.sqlbase import SQLBase, sqlvalues
283 from lp.soyuz.adapters.archivedependencies import (
284 get_primary_current_component, get_sources_list_for_building)
285@@ -54,6 +59,7 @@
286 PackagePublishingStatus)
287 from lp.soyuz.model.buildpackagejob import BuildPackageJob
288 from canonical.launchpad.webapp import urlappend
289+from canonical.lazr.utils import safe_hasattr
290 from canonical.librarian.utils import copy_and_close
291
292
293@@ -114,6 +120,7 @@
294 class Builder(SQLBase):
295
296 implements(IBuilder, IHasBuildRecords)
297+ delegates(IBuildFarmJobBehavior, context="current_build_behavior")
298 _table = 'Builder'
299
300 _defaultOrder = ['id']
301@@ -135,6 +142,51 @@
302 vm_host = StringCol(dbName='vm_host')
303 active = BoolCol(dbName='active', notNull=True, default=True)
304
305+ def _getCurrentBuildBehavior(self):
306+ """Return the current build behavior."""
307+ if not safe_hasattr(self, '_current_build_behavior'):
308+ self._current_build_behavior = None
309+
310+ if (self._current_build_behavior is None or
311+ isinstance(self._current_build_behavior, IdleBuildBehavior)):
312+ # If we don't currently have a current build behavior set,
313+ # or we are currently idle, then...
314+ currentjob = self.currentjob
315+ if currentjob is not None:
316+ # ...we'll set it based on our current job.
317+ self._current_build_behavior = (
318+ currentjob.required_build_behavior)
319+ self._current_build_behavior.setBuilder(self)
320+ return self._current_build_behavior
321+ elif self._current_build_behavior is None:
322+ # If we don't have a current job or an idle behavior
323+ # already set, then we just set the idle behavior
324+ # before returning.
325+ self._current_build_behavior = IdleBuildBehavior()
326+ return self._current_build_behavior
327+
328+ else:
329+ # We did have a current non-idle build behavior set, so
330+ # we just return it.
331+ return self._current_build_behavior
332+
333+
334+ def _setCurrentBuildBehavior(self, new_behavior):
335+ """Set the current build behavior."""
336+
337+ if self.currentjob is not None:
338+ # We do not allow the current build behavior to be reset if we
339+ # have a current job.
340+ raise BuildBehaviorMismatch(
341+ "Attempt to reset builder behavior while a current build "
342+ "exists.")
343+ else:
344+ self._current_build_behavior = new_behavior
345+ self._current_build_behavior.setBuilder(self)
346+
347+ current_build_behavior = property(
348+ _getCurrentBuildBehavior, _setCurrentBuildBehavior)
349+
350 def cacheFileOnSlave(self, logger, libraryfilealias):
351 """See `IBuilder`."""
352 url = libraryfilealias.http_url
353@@ -351,10 +403,9 @@
354
355 def startBuild(self, build_queue_item, logger):
356 """See IBuilder."""
357- build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
358- spr = build.sourcepackagerelease
359- logger.info("startBuild(%s, %s, %s, %s)", self.url,
360- spr.name, spr.version, build.pocket.title)
361+ # Set the build behavior depending on the provided build queue item.
362+ self.current_build_behavior = build_queue_item.required_build_behavior
363+ self.logStartBuild(build_queue_item, logger)
364
365 # Make sure the request is valid; an exception is raised if it's not.
366 self._verifyBuildRequest(build_queue_item, logger)
367
368=== modified file 'lib/lp/soyuz/model/buildqueue.py'
369--- lib/lp/soyuz/model/buildqueue.py 2009-11-20 18:06:28 +0000
370+++ lib/lp/soyuz/model/buildqueue.py 2009-12-01 16:47:13 +0000
371@@ -24,6 +24,8 @@
372 from canonical.database.sqlbase import SQLBase, sqlvalues
373 from canonical.launchpad.webapp.interfaces import NotFoundError
374 from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
375+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
376+ IBuildFarmJobBehavior)
377 from lp.services.job.interfaces.job import JobStatus
378 from lp.services.job.model.job import Job
379 from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
380@@ -49,6 +51,11 @@
381 estimated_duration = IntervalCol()
382
383 @property
384+ def required_build_behavior(self):
385+ """See `IBuildQueue`."""
386+ return IBuildFarmJobBehavior(self.specific_job)
387+
388+ @property
389 def specific_job(self):
390 """See `IBuildQueue`."""
391 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
392
393=== modified file 'lib/lp/soyuz/tests/test_builder.py'
394--- lib/lp/soyuz/tests/test_builder.py 2009-11-13 16:37:05 +0000
395+++ lib/lp/soyuz/tests/test_builder.py 2009-12-01 16:47:13 +0000
396@@ -8,10 +8,15 @@
397 from zope.component import getUtility
398
399 from canonical.testing import LaunchpadZopelessLayer
400+from lp.buildmaster.interfaces.buildfarmjobbehavior import (
401+ BuildBehaviorMismatch, IBuildFarmJobBehavior)
402+from lp.buildmaster.model.buildfarmjobbehavior import IdleBuildBehavior
403 from lp.soyuz.interfaces.archive import ArchivePurpose
404 from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
405 from lp.soyuz.interfaces.builder import IBuilderSet
406 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
407+from lp.soyuz.model.binarypackagebuildbehavior import (
408+ BinaryPackageBuildBehavior)
409 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
410 from lp.testing import TestCaseWithFactory
411
412@@ -214,5 +219,69 @@
413 self.failUnlessEqual('primary', build.archive.name)
414 self.failUnlessEqual('firefox', build.sourcepackagerelease.name)
415
416+
417+class TestCurrentBuildBehavior(TestCaseWithFactory):
418+ """This test ensures the get/set behavior of IBuilder's
419+ current_build_behavior property.
420+ """
421+
422+ layer = LaunchpadZopelessLayer
423+
424+ def setUp(self):
425+ """Create a new builder ready for testing."""
426+ super(TestCurrentBuildBehavior, self).setUp()
427+ self.builder = self.factory.makeBuilder(name='builder')
428+
429+ # Have a publisher and a ppa handy for some of the tests below.
430+ self.publisher = SoyuzTestPublisher()
431+ self.publisher.prepareBreezyAutotest()
432+ self.ppa_joe = self.factory.makeArchive(name="joesppa")
433+
434+ self.build = self.publisher.getPubSource(
435+ sourcename="gedit", status=PackagePublishingStatus.PUBLISHED,
436+ archive=self.ppa_joe).createMissingBuilds()[0]
437+
438+ self.buildfarmjob = self.build.buildqueue_record.specific_job
439+
440+ def test_idle_behavior_when_no_current_build(self):
441+ """We return an idle behavior when there is no behavior specified
442+ nor a current build.
443+ """
444+ self.assertIsInstance(
445+ self.builder.current_build_behavior, IdleBuildBehavior)
446+
447+ def test_set_behavior_when_no_current_job(self):
448+ """If a builder is idle then it is possible to set the behavior."""
449+ self.builder.current_build_behavior = IBuildFarmJobBehavior(
450+ self.buildfarmjob)
451+
452+ self.assertIsInstance(
453+ self.builder.current_build_behavior, BinaryPackageBuildBehavior)
454+
455+ def test_current_job_behavior(self):
456+ """The current behavior is set automatically from the current job."""
457+ # Set the builder attribute on the buildqueue record so that our
458+ # builder will think it has a current build.
459+ self.build.buildqueue_record.builder = self.builder
460+
461+ self.assertIsInstance(
462+ self.builder.current_build_behavior, BinaryPackageBuildBehavior)
463+
464+ def test_set_behavior_when_current_job(self):
465+ """If a builder has a current job then it's behavior cannot be set.
466+ """
467+ self.build.buildqueue_record.builder = self.builder
468+
469+ # As we can't use assertRaises for a property, we use a try-except
470+ # instead.
471+ assertion_raised = False
472+ try:
473+ self.builder.current_build_behavior = IBuildFarmJobBehavior(
474+ self.buildfarmjob)
475+ except BuildBehaviorMismatch, e:
476+ assertion_raised = True
477+
478+ self.failUnless(assertion_raised)
479+
480 def test_suite():
481 return unittest.TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches

to status/vote changes: