Merge lp:~mbp/launchpad/use-txfixtures into lp:launchpad

Proposed by Martin Pool
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 14263
Proposed branch: lp:~mbp/launchpad/use-txfixtures
Merge into: lp:launchpad
Prerequisite: lp:~mbp/launchpad/800295-buildd-split
Diff against target: 249 lines (+16/-133)
5 files modified
lib/canonical/buildd/tests/harness.py (+4/-5)
lib/canonical/launchpad/daemons/tachandler.py (+9/-127)
lib/canonical/launchpad/daemons/tests/test_tachandler.py (+1/-1)
setup.py (+1/-0)
versions.cfg (+1/-0)
To merge this branch: bzr merge lp:~mbp/launchpad/use-txfixtures
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+81243@code.launchpad.net

Commit message

[r=bac][no-qa] split out and depend upon txfixtures

Description of the change

This builds on <https://code.launchpad.net/~mbp/launchpad/800295-buildd-split/+merge/81111> to do more separation of the buildd code from the Launchpad tree.

The only remaining code dependency of the buildds is that they need a test fixture to run a Twisted application out-of-process. I have extracted out that code into lp:txfixtures.

This patch then:

 * makes Launchpad depend on txfixtures
 * makes txfixtures.TacTestFixture the base for Launchpad's own TacTestSetup
 * corrects in passing a test that counts on raising a DeprecationWarning for what's actually an operational warning (stale pid file) and that can be flaky on pythons where that is automatically suppressed
 * changes the buildd code to use txfixtures directly rather than canonical.launchpad.daemons.tachandler, so it no longer needs anything from the lp tree

I thought about changing buildd to use only relative imports so that it obviously didn't use anything from the rest of the tree and it could be more easily moved, but in the light of <https://code.launchpad.net/~abentley/launchpad/absolute-oops-import/+merge/71423> I decided not to.

This doesn't actually move the buildd out of tree but that should now be a small step.

I've left most of the tac fixture tests in the tree - they are somewhat redundant with tests now done inside txfixtures, but they're cheap and I think the integration testing is worthwhile, at least for now.

Launchpad's TacTestSetup could probably be slimmed down a bit more or even deleted entirely.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

At least one more thing is needed before this can land: package txfixtures
and make that a dpkg dependency of buildd.

Revision history for this message
Brad Crittenden (bac) wrote :

Hi Martin,

Thanks for the work you're doing here to untangle things. Your changes look good.

review: Approve (code)
Revision history for this message
Robert Collins (lifeless) wrote :

FTR you don't need to debian package txfixtures.

Add the egg to the download cache, and we're GTG.

Revision history for this message
Martin Pool (mbp) wrote :

On 7 November 2011 18:28, Robert Collins <email address hidden> wrote:
> FTR you don't need to debian package txfixtures.
>
> Add the egg to the download cache, and we're GTG.

For deployment into lp itself, yes. But this will also need to be
installed alongside buildds, which are installed from a package. So
we either need txfixtures packaged, or at least to get lamont & co to
agree to install it some other way.

Revision history for this message
Martin Pool (mbp) wrote :

I needed some additional changes in txfixtures to make this work on python<2.7, since they don't understand PYTHONWARNINGS and deprecation warnings make things fail.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/buildd/tests/harness.py'
--- lib/canonical/buildd/tests/harness.py 2011-11-04 00:43:11 +0000
+++ lib/canonical/buildd/tests/harness.py 2011-11-07 07:50:31 +0000
@@ -11,11 +11,9 @@
11import unittest11import unittest
12from ConfigParser import SafeConfigParser12from ConfigParser import SafeConfigParser
1313
14import canonical14from txfixtures.tachandler import TacTestFixture
15import canonical.buildd
1615
17from canonical.buildd.slave import BuildDSlave16from canonical.buildd.slave import BuildDSlave
18from canonical.launchpad.daemons.tachandler import TacTestSetup
1917
20from lp.services.osutils import remove_tree18from lp.services.osutils import remove_tree
2119
@@ -58,7 +56,7 @@
58 f.close()56 f.close()
5957
6058
61class BuilddSlaveTestSetup(TacTestSetup):59class BuilddSlaveTestSetup(TacTestFixture):
62 r"""Setup BuildSlave for use by functional tests60 r"""Setup BuildSlave for use by functional tests
6361
64 >>> fixture = BuilddSlaveTestSetup()62 >>> fixture = BuilddSlaveTestSetup()
@@ -119,7 +117,8 @@
119 @property117 @property
120 def tacfile(self):118 def tacfile(self):
121 return os.path.abspath(os.path.join(119 return os.path.abspath(os.path.join(
122 os.path.dirname(canonical.buildd.__file__),120 os.path.dirname(__file__),
121 os.path.pardir,
123 'buildd-slave.tac'122 'buildd-slave.tac'
124 ))123 ))
125124
126125
=== modified file 'lib/canonical/launchpad/daemons/tachandler.py'
--- lib/canonical/launchpad/daemons/tachandler.py 2011-11-04 00:43:11 +0000
+++ lib/canonical/launchpad/daemons/tachandler.py 2011-11-07 07:50:31 +0000
@@ -21,6 +21,11 @@
2121
22from fixtures import Fixture22from fixtures import Fixture
2323
24from txfixtures.tachandler import (
25 TacException,
26 TacTestFixture,
27 )
28
24from canonical.launchpad.daemons import readyservice29from canonical.launchpad.daemons import readyservice
25from lp.services.osutils import (30from lp.services.osutils import (
26 get_pid_from_file,31 get_pid_from_file,
@@ -36,74 +41,22 @@
36 os.pardir, os.pardir, os.pardir, os.pardir, 'bin', 'twistd'))41 os.pardir, os.pardir, os.pardir, os.pardir, 'bin', 'twistd'))
3742
3843
39class TacException(Exception):44class TacTestSetup(TacTestFixture):
40 """Error raised by TacTestSetup."""
41
42
43class TacTestSetup(Fixture):
44 """Setup an TAC file as daemon for use by functional tests.45 """Setup an TAC file as daemon for use by functional tests.
4546
46 You must override setUpRoot to set up a root directory for the daemon.47 You must override setUpRoot to set up a root directory for the daemon.
47 """48 """
4849
49 def setUp(self, spew=False, umask=None):50 def setUp(self, spew=False, umask=None):
50 Fixture.setUp(self)
51 if get_pid_from_file(self.pidfile):
52 # An attempt to run while there was an existing live helper
53 # was made. Note that this races with helpers which use unique
54 # roots, so when moving/eliminating this code check subclasses
55 # for workarounds and remove those too.
56 pid = get_pid_from_file(self.pidfile)
57 warnings.warn("Attempt to start Tachandler with an existing "
58 "instance (%d) running in %s." % (pid, self.pidfile),
59 DeprecationWarning, stacklevel=2)
60 two_stage_kill(pid)
61 # If the pid file still exists, it may indicate that the process
62 # respawned itself, or that two processes were started (race?) and
63 # one is still running while the other has ended, or the process
64 # was killed but it didn't remove the pid file (bug), or the
65 # machine was hard-rebooted and the pid file was not cleaned up
66 # (bug again). In other words, it's not safe to assume that a
67 # stale pid file is safe to delete without human intervention.
68 if get_pid_from_file(self.pidfile):
69 raise TacException(
70 "Could not kill stale process %s." % (self.pidfile,))
71
72 # setUp() watches the logfile to determine when the daemon has fully51 # setUp() watches the logfile to determine when the daemon has fully
73 # started. If it sees an old logfile, then it will find the52 # started. If it sees an old logfile, then it will find the
74 # readyservice.LOG_MAGIC string and return immediately, provoking53 # readyservice.LOG_MAGIC string and return immediately, provoking
75 # hard-to-diagnose race conditions. Delete the logfile to make sure54 # hard-to-diagnose race conditions. Delete the logfile to make sure
76 # this does not happen.55 # this does not happen.
77 remove_if_exists(self.logfile)56 remove_if_exists(self.logfile)
7857 TacTestFixture.setUp(self,
79 self.setUpRoot()58 python_path=sys.executable,
80 args = [sys.executable,59 twistd_script=twistd_script)
81 # XXX: 2010-04-26, Salgado, bug=570246: Deprecation warnings
82 # in Twisted are not our problem. They also aren't easy to
83 # suppress, and cause test failures due to spurious stderr
84 # output. Just shut the whole bloody mess up.
85 '-Wignore::DeprecationWarning',
86 twistd_script, '-o', '-y', self.tacfile,
87 '--pidfile', self.pidfile, '--logfile', self.logfile]
88 if spew:
89 args.append('--spew')
90 if umask is not None:
91 args.extend(('--umask', umask))
92
93 # Run twistd, and raise an error if the return value is non-zero or
94 # stdout/stderr are written to.
95 proc = subprocess.Popen(args, stdout=subprocess.PIPE,
96 stderr=subprocess.STDOUT)
97 self.addCleanup(self.killTac)
98 stdout = until_no_eintr(10, proc.stdout.read)
99 if stdout:
100 raise TacException('Error running %s: unclean stdout/err: %s'
101 % (args, stdout))
102 rv = proc.wait()
103 if rv != 0:
104 raise TacException('Error %d running %s' % (rv, args))
105
106 self._waitForDaemonStartup()
10760
108 def _hasDaemonStarted(self):61 def _hasDaemonStarted(self):
109 """Has the daemon started?62 """Has the daemon started?
@@ -117,61 +70,6 @@
117 else:70 else:
118 return False71 return False
11972
120 def _isPortListening(self, host, port):
121 """True if a tcp port is accepting connections.
122
123 This can be used by subclasses overriding _hasDaemonStarted, if they
124 want to check the port is up rather than for the contents of the log
125 file.
126 """
127 try:
128 s = socket.socket()
129 s.settimeout(2.0)
130 s.connect((host, port))
131 s.close()
132 return True
133 except socket.error, e:
134 if e.errno == errno.ECONNREFUSED:
135 return False
136 else:
137 raise
138
139 def _waitForDaemonStartup(self):
140 """ Wait for the daemon to fully start.
141
142 Times out after 20 seconds. If that happens, the log file content
143 will be included in the exception message for debugging purpose.
144
145 :raises TacException: Timeout.
146 """
147 # Watch the log file for readyservice.LOG_MAGIC to signal that startup
148 # has completed.
149 now = time.time()
150 deadline = now + 20
151 while now < deadline and not self._hasDaemonStarted():
152 time.sleep(0.1)
153 now = time.time()
154
155 if now >= deadline:
156 raise TacException('Unable to start %s. Content of %s:\n%s' % (
157 self.tacfile, self.logfile, open(self.logfile).read()))
158
159 def tearDown(self):
160 # For compatibility - migrate to cleanUp.
161 self.cleanUp()
162
163 def killTac(self):
164 """Kill the TAC file if it is running."""
165 pidfile = self.pidfile
166 kill_by_pidfile(pidfile)
167
168 def sendSignal(self, sig):
169 """Send the given signal to the tac process."""
170 pid = get_pid_from_file(self.pidfile)
171 if pid is None:
172 return
173 os.kill(pid, sig)
174
175 def truncateLog(self):73 def truncateLog(self):
176 """Truncate the log file.74 """Truncate the log file.
17775
@@ -198,19 +96,3 @@
198 test failure (e.g. log files might contain helpful tracebacks).96 test failure (e.g. log files might contain helpful tracebacks).
199 """97 """
200 raise NotImplementedError98 raise NotImplementedError
201
202 @property
203 def root(self):
204 raise NotImplementedError
205
206 @property
207 def tacfile(self):
208 raise NotImplementedError
209
210 @property
211 def pidfile(self):
212 raise NotImplementedError
213
214 @property
215 def logfile(self):
216 raise NotImplementedError
21799
=== modified file 'lib/canonical/launchpad/daemons/tests/test_tachandler.py'
--- lib/canonical/launchpad/daemons/tests/test_tachandler.py 2011-08-19 15:36:31 +0000
+++ lib/canonical/launchpad/daemons/tests/test_tachandler.py 2011-11-07 07:50:31 +0000
@@ -132,7 +132,7 @@
132132
133 # One deprecation warning is emitted.133 # One deprecation warning is emitted.
134 self.assertEqual(1, len(warnings_log))134 self.assertEqual(1, len(warnings_log))
135 self.assertIs(DeprecationWarning, warnings_log[0].category)135 self.assertIs(UserWarning, warnings_log[0].category)
136136
137 def test_truncateLog(self):137 def test_truncateLog(self):
138 """138 """
139139
=== modified file 'setup.py'
--- setup.py 2011-11-03 23:03:26 +0000
+++ setup.py 2011-11-07 07:50:31 +0000
@@ -86,6 +86,7 @@
86 'timeline',86 'timeline',
87 'transaction',87 'transaction',
88 'Twisted',88 'Twisted',
89 'txfixtures',
89 'txlongpollfixture',90 'txlongpollfixture',
90 'wadllib',91 'wadllib',
91 'z3c.pt',92 'z3c.pt',
9293
=== modified file 'versions.cfg'
--- versions.cfg 2011-11-07 03:44:49 +0000
+++ versions.cfg 2011-11-07 07:50:31 +0000
@@ -98,6 +98,7 @@
98timeline = 0.0.198timeline = 0.0.1
99transaction = 1.0.099transaction = 1.0.0
100txamqp = 0.4100txamqp = 0.4
101txfixtures = 0.1.4
101txlongpoll = 0.2.9102txlongpoll = 0.2.9
102txlongpollfixture = 0.1.3103txlongpollfixture = 0.1.3
103Twisted = 11.1.0pre1104Twisted = 11.1.0pre1