Merge branch 'invalid-pidfile' into delete-invalid-pidfile--lpirl

Conflicts:
	src/allmydata/scripts/startstop_node.py
	src/allmydata/test/cli/test_cli.py
This commit is contained in:
meejah 2017-11-08 13:30:00 -07:00
commit 50f8397c99
3 changed files with 71 additions and 13 deletions

View File

@ -10,6 +10,35 @@ from allmydata.util.encodingutil import listdir_unicode, quote_local_unicode_pat
from twisted.application.service import Service
def get_pidfile(basedir):
"""
Returns the path to the PID file.
:param basedir: the node's base directory
:returns: the path to the PID file
"""
return os.path.join(basedir, u"twistd.pid")
def get_pid_from_pidfile(pidfile):
"""
Tries to read and return the PID stored in the node's PID file
(twistd.pid).
:param pidfile: try to read this PID file
:returns: A numeric PID on success, ``None`` if PID file absent or
inaccessible, ``-1`` if PID file invalid.
"""
try:
with open(pidfile, "r") as f:
pid = f.read()
except EnvironmentError:
return None
try:
pid = int(pid)
except ValueError:
return -1
return pid
def identify_node_type(basedir):
"""
:return unicode: None or one of: 'client', 'introducer',
@ -155,6 +184,12 @@ def daemonize(config):
return 1
twistd_config.loadedPlugins = {"DaemonizeTahoeNode": DaemonizeTahoeNodePlugin(nodetype, basedir)}
# handle invalid PID file (twistd might not start otherwise)
pidfile = get_pidfile(basedir)
if get_pid_from_pidfile(pidfile) == -1:
print >>err, "found invalid PID file in %s - deleting it" % basedir
os.remove(pidfile)
# On Unix-like platforms:
# Unless --nodaemon was provided, the twistd.runApp() below spawns off a
# child process, and the parent calls os._exit(0), so there's no way for

View File

@ -4,6 +4,7 @@ import signal
from allmydata.scripts.common import BasedirOptions
from allmydata.util.encodingutil import quote_local_unicode_path
from .tahoe_daemonize import get_pidfile, get_pid_from_pidfile
COULD_NOT_STOP = 2
@ -23,21 +24,15 @@ def stop(config):
basedir = config['basedir']
quoted_basedir = quote_local_unicode_path(basedir)
print >>out, "STOPPING", quoted_basedir
pidfile = os.path.join(basedir, u"twistd.pid")
if not os.path.exists(pidfile):
pidfile = get_pidfile(basedir)
pid = get_pid_from_pidfile(pidfile)
if pid is None:
print >>err, "%s does not look like a running node directory (no twistd.pid)" % quoted_basedir
# we define rc=2 to mean "nothing is running, but it wasn't me who
# stopped it"
return COULD_NOT_STOP
with open(pidfile, "r") as f:
pid = f.read()
try:
pid = int(pid)
except ValueError:
# The error message below mimics a Twisted error message, which is
# displayed when starting a node with an invalid pidfile.
print >>err, "Pidfile %s contains non-numeric value" % pidfile
return 2
elif pid == -1:
print >>err, "%s contains an invalid PID file" % basedir
# we define rc=2 to mean "nothing is running, but it wasn't me who
# stopped it"
return 2

View File

@ -3,6 +3,7 @@ import os.path
from cStringIO import StringIO
import urllib, sys
import re
from mock import patch
from twisted.trial import unittest
from twisted.python.monkey import MonkeyPatcher
@ -1312,4 +1313,31 @@ class Stop(unittest.TestCase):
result_code = tahoe_stop.stop(config)
self.assertEqual(2, result_code)
self.assertIn("contains non-numeric value", config.stderr.getvalue())
self.assertIn("invalid PID file", config.stderr.getvalue())
class Start(unittest.TestCase):
@patch('allmydata.scripts.tahoe_daemonize.os.chdir')
@patch('allmydata.scripts.tahoe_daemonize.twistd')
def test_non_numeric_pid(self, mock_twistd, chdir):
"""
If the pidfile exists but does not contain a numeric value, a complaint to
this effect is written to stderr.
"""
basedir = FilePath(self.mktemp().decode("ascii"))
basedir.makedirs()
basedir.child(u"twistd.pid").setContent(b"foo")
basedir.child(u"tahoe-client.tac").setContent(b"")
config = tahoe_daemonize.DaemonizeOptions()
config.stdout = StringIO()
config.stderr = StringIO()
config['basedir'] = basedir.path
config.twistd_args = []
result_code = tahoe_daemonize.daemonize(config)
self.assertIn("invalid PID file", config.stderr.getvalue())
self.assertTrue(len(mock_twistd.mock_calls), 1)
self.assertEqual(mock_twistd.mock_calls[0][0], 'runApp')
self.assertEqual(0, result_code)