Merge lp:~cmiller/desktopcouch/getport-at-call-time into lp:desktopcouch

Proposed by Chad Miller
Status: Superseded
Proposed branch: lp:~cmiller/desktopcouch/getport-at-call-time
Merge into: lp:desktopcouch
Diff against target: None lines
To merge this branch: bzr merge lp:~cmiller/desktopcouch/getport-at-call-time
Reviewer Review Type Date Requested Status
Ubuntu One hackers Pending
Review via email: mp+10104@code.launchpad.net

This proposal has been superseded by a proposal from 2009-08-13.

To post a comment you must log in.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'desktopcouch/__init__.py'
2--- desktopcouch/__init__.py 2009-08-04 11:15:52 +0000
3+++ desktopcouch/__init__.py 2009-08-13 12:12:22 +0000
4@@ -21,52 +21,66 @@
5 import errno
6 import time
7
8-def find_pid():
9+def find_pid(start_if_not_running=True):
10 # Work out whether CouchDB is running by looking at its pid file
11- from desktopcouch import local_files
12- pid = ''
13- try:
14- fp = open(local_files.FILE_PID)
15- pid = int(fp.read())
16- fp.close()
17- except IOError:
18- pass
19+ def get_pid():
20+ from desktopcouch import local_files
21+ try:
22+ pid_file = local_files.FILE_PID
23+ with open(pid_file) as fp:
24+ try:
25+ return int(fp.read())
26+ except ValueError:
27+ return None
28+ except IOError:
29+ return None
30
31- if not is_couchdb(pid):
32+ pid = get_pid()
33+ if not process_is_couchdb(pid) and start_if_not_running:
34 # pidfile is stale
35 # start CouchDB by running the startup script
36- print "Desktop CouchDB is not running; starting it."
37+ print "Desktop CouchDB is not running; starting it.",
38 from desktopcouch import start_local_couchdb
39 start_local_couchdb.start_couchdb()
40- time.sleep(2) # give the process a chance to start
41+ for timeout in xrange(1000):
42+ pid = get_pid()
43+ if process_is_couchdb(pid):
44+ break
45+ print ".",
46+ time.sleep(0.1)
47
48- # get the pid
49- try:
50- with open(local_files.FILE_PID) as pid_file:
51- pid = int(pid_file.read().strip())
52- except IOError, e:
53- if e.errno == ENOENT:
54+ if process_is_couchdb(pid):
55+ print " done."
56+ else:
57+ print " failed."
58 raise RuntimeError("desktop-couch not started")
59- else:
60- raise
61+
62 return pid
63
64-def is_couchdb(pid):
65+def process_is_couchdb__linux(pid):
66+ if not isinstance(pid, int):
67+ return False
68+
69 proc_dir = "/proc/%s" % (pid,)
70
71- # check to make sure that the process still exists
72- if not os.path.isdir(proc_dir):
73- return False
74-
75- # check to make sure it is actually a desktop-couch instance
76- with open(os.path.join(proc_dir, 'cmdline')) as cmd_file:
77- cmd = cmd_file.read()
78- if re.search('desktop-couch', cmd) is None:
79+ try:
80+ # check to make sure it is actually a desktop-couch instance
81+ with open(os.path.join(proc_dir, 'cmdline')) as cmd_file:
82+ cmd = cmd_file.read()
83+ if '/desktop-couch' not in cmd:
84+ return False
85+
86+ # make sure it's our process.
87+ if not os.access(os.path.join(proc_dir, "mem"), os.W_OK):
88+ return False
89+
90+ except IOError:
91 return False
92
93 return True
94
95-def find_port(pid):
96+
97+def find_port__linux(pid):
98 # Look in the CouchDB log to find the port number, someday.
99 # Currently, we have to grovel around in /proc instead.
100 # Oh, the huge manatee... (this replaced an lsof shell recipe
101@@ -76,8 +90,12 @@
102
103 # enumerate the process' file descriptors
104 fd_dir = os.path.join(proc_dir, 'fd')
105- fd_paths = [os.readlink(os.path.join(fd_dir, fd))
106- for fd in os.listdir(fd_dir)]
107+ try:
108+ fd_paths = [os.readlink(os.path.join(fd_dir, fd))
109+ for fd in os.listdir(fd_dir)]
110+ except OSError:
111+ raise RuntimeError("Unable to find file descriptors in /proc")
112+
113
114 # identify socket fds
115 socket_matches = [re.match('socket:\\[([0-9]+)\\]', p) for p in fd_paths]
116@@ -108,9 +126,24 @@
117 for line in tcp_file:
118 match = listening_regexp.match(line)
119 if match is not None:
120- port = int(match.group(1), 16)
121+ port = str(int(match.group(1), 16))
122 break
123 if port is None:
124 raise RuntimeError("Unable to find listening port")
125
126- return str(port)
127+ return port
128+
129+
130+
131+import platform
132+os_name = platform.system()
133+try:
134+ process_is_couchdb = {
135+ "Linux":process_is_couchdb__linux
136+ } [os_name]
137+
138+ find_port = {
139+ "Linux":find_port__linux
140+ } [os_name]
141+except KeyError:
142+ raise NotImplementedError("os %r is not yet supported" % (os_name,))
143
144=== modified file 'desktopcouch/records/tests/test_field_registry.py'
145--- desktopcouch/records/tests/test_field_registry.py 2009-07-08 17:48:11 +0000
146+++ desktopcouch/records/tests/test_field_registry.py 2009-08-13 16:06:20 +0000
147@@ -18,7 +18,7 @@
148 """Test cases for field mapping"""
149
150 import copy
151-from twisted.trial.unittest import TestCase as TwistedTestCase
152+from testtools import TestCase
153 from desktopcouch.records.field_registry import (
154 SimpleFieldMapping, MergeableListFieldMapping, Transformer)
155 from desktopcouch.records.record import Record
156@@ -53,7 +53,7 @@
157 super(AppTransformer, self).__init__('Test App', field_registry)
158
159
160-class TestFieldMapping(TwistedTestCase):
161+class TestFieldMapping(TestCase):
162 """Test Case for FieldMapping objects."""
163
164 def setUp(self):
165@@ -85,7 +85,7 @@
166 self.assertEqual(None, mapping.getValue(record))
167
168
169-class TestTransformer(TwistedTestCase):
170+class TestTransformer(TestCase):
171 """Test application specific transformer classes"""
172
173 def setUp(self):
174
175=== modified file 'desktopcouch/records/tests/test_record.py'
176--- desktopcouch/records/tests/test_record.py 2009-07-08 17:48:11 +0000
177+++ desktopcouch/records/tests/test_record.py 2009-08-13 16:06:20 +0000
178@@ -18,7 +18,7 @@
179
180 """Tests for the RecordDict object on which the Contacts API is built."""
181
182-from twisted.trial.unittest import TestCase as TwistedTestCase
183+from testtools import TestCase
184
185 # pylint does not like relative imports from containing packages
186 # pylint: disable-msg=F0401
187@@ -26,7 +26,7 @@
188 record_factory, IllegalKeyException, validate)
189
190
191-class TestRecords(TwistedTestCase):
192+class TestRecords(TestCase):
193 """Test the record functionality"""
194
195 def setUp(self):
196@@ -180,7 +180,7 @@
197 self.record.record_type)
198
199
200-class TestRecordFactory(TwistedTestCase):
201+class TestRecordFactory(TestCase):
202 """Test Record/Mergeable List factories."""
203
204 def setUp(self):
205
206=== modified file 'desktopcouch/records/tests/test_server.py'
207--- desktopcouch/records/tests/test_server.py 2009-08-10 21:32:52 +0000
208+++ desktopcouch/records/tests/test_server.py 2009-08-12 14:26:40 +0000
209@@ -19,7 +19,8 @@
210 """testing database/contact.py module"""
211
212 import testtools
213-
214+import random
215+from desktopcouch.stop_local_couchdb import stop_couchdb
216 from desktopcouch.records.server import CouchDatabase
217 from desktopcouch.records.record import Record
218
219@@ -49,6 +50,9 @@
220 def tearDown(self):
221 """tear down each test"""
222 del self.database._server[self.dbname]
223+ if random.choice([1,2,3,4]) == 3: # don't harass it unnecessarily
224+ print u"\u2620", # death
225+ stop_couchdb()
226
227 def test_get_records_by_record_type_save_view(self):
228 """Test getting mutliple records by type"""
229
230=== modified file 'desktopcouch/start_local_couchdb.py' (properties changed: -x to +x)
231--- desktopcouch/start_local_couchdb.py 2009-07-27 18:16:19 +0000
232+++ desktopcouch/start_local_couchdb.py 2009-08-13 12:14:05 +0000
233@@ -32,10 +32,12 @@
234 """
235
236 from __future__ import with_statement
237-import os, subprocess, sys
238+import os, sys
239+import subprocess
240 import desktopcouch
241 from desktopcouch import local_files
242 import xdg.BaseDirectory
243+import errno
244 import time
245
246 def dump_ini(data, filename):
247@@ -52,8 +54,6 @@
248 fd.write("\n")
249 fd.close()
250
251-
252-
253 def create_ini_file():
254 """Write CouchDB ini file if not already present"""
255 # FIXME add update trigger folder
256@@ -89,7 +89,16 @@
257 """Actually start the CouchDB process"""
258 local_exec = local_files.COUCH_EXEC_COMMAND + ['-b']
259 try:
260- retcode = subprocess.call(local_exec, shell=False)
261+ # subprocess is buggy. Chad patched, but that takes time to propagate.
262+ proc = subprocess.Popen(local_exec)
263+ while True:
264+ try:
265+ retcode = proc.wait()
266+ break
267+ except OSError, e:
268+ if e.errno == errno.EINTR:
269+ continue
270+ raise
271 if retcode < 0:
272 print >> sys.stderr, "Child was terminated by signal", -retcode
273 elif retcode > 0:
274@@ -118,15 +127,30 @@
275 html = fp.read()
276 fp.close()
277
278- time.sleep(1)
279- pid = desktopcouch.find_pid()
280- port = desktopcouch.find_port(pid)
281+ port = None
282+ for retry in xrange(10000, 0, -1):
283+ pid = desktopcouch.find_pid(start_if_not_running=False)
284+ try:
285+ port = desktopcouch.find_port(pid)
286+ break
287+ except RuntimeError, e:
288+ if retry == 1:
289+ raise
290+ time.sleep(0.01)
291+ continue
292
293- fp = open(bookmark_file, "w")
294- fp.write(html.replace("[[COUCHDB_PORT]]", port))
295- fp.close()
296- print "Browse your desktop CouchDB at file://%s" % \
297- os.path.realpath(bookmark_file)
298+ if port is None:
299+ print "We couldn't find desktop-CouchDB's network port. Bookmark file not written."
300+ try:
301+ os.remove(bookmark_file)
302+ except OSError:
303+ pass
304+ else:
305+ fp = open(bookmark_file, "w")
306+ fp.write(html.replace("[[COUCHDB_PORT]]", port))
307+ fp.close()
308+ print "Browse your desktop CouchDB at file://%s" % \
309+ os.path.realpath(bookmark_file)
310
311 def start_couchdb():
312 """Execute each step to start a desktop CouchDB"""
313@@ -135,6 +159,7 @@
314 update_design_documents()
315 write_bookmark_file()
316
317+
318 if __name__ == "__main__":
319 start_couchdb()
320 print "Desktop CouchDB started"
321
322=== added file 'desktopcouch/stop_local_couchdb.py'
323--- desktopcouch/stop_local_couchdb.py 1970-01-01 00:00:00 +0000
324+++ desktopcouch/stop_local_couchdb.py 2009-08-12 14:26:14 +0000
325@@ -0,0 +1,49 @@
326+#!/usr/bin/python
327+# Copyright 2009 Canonical Ltd.
328+#
329+# This file is part of desktopcouch.
330+#
331+# desktopcouch is free software: you can redistribute it and/or modify
332+# it under the terms of the GNU Lesser General Public License version 3
333+# as published by the Free Software Foundation.
334+#
335+# desktopcouch is distributed in the hope that it will be useful,
336+# but WITHOUT ANY WARRANTY; without even the implied warranty of
337+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
338+# GNU Lesser General Public License for more details.
339+#
340+# You should have received a copy of the GNU Lesser General Public License
341+# along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
342+#
343+# Author: Chad Miller <chad.miller@canonical.com>
344+"""
345+Stop local CouchDB server.
346+"""
347+
348+import os
349+import desktopcouch
350+import time
351+import signal
352+import errno
353+
354+def stop_couchdb():
355+ pid = desktopcouch.find_pid(start_if_not_running=False)
356+ while pid is not None:
357+ try:
358+ os.kill(pid, signal.SIGTERM)
359+ except OSError, e:
360+ if e.errno == errno.ESRCH:
361+ break
362+ raise
363+
364+ for retry in xrange(300):
365+ try:
366+ os.kill(pid, 0) # test existence. sig-zero is special.
367+ except OSError:
368+ break
369+ time.sleep(0.01)
370+
371+ pid = desktopcouch.find_pid(start_if_not_running=False)
372+
373+if __name__ == "__main__":
374+ stop_couchdb()

Subscribers

People subscribed via source and target branches