Browse Source

Fixed a bug where opening the HTTP servers would fail silently
for socket errors other than errno.EADDRINUSE.

Mike Naberezny 17 years ago
parent
commit
3b4166b437
3 changed files with 106 additions and 2 deletions
  1. 3 0
      CHANGES.txt
  2. 13 2
      src/supervisor/options.py
  3. 90 0
      src/supervisor/tests/test_options.py

+ 3 - 0
CHANGES.txt

@@ -1,4 +1,7 @@
 Next Release
+  - Fixed a bug where opening the HTTP servers would fail silently
+    for socket errors other than errno.EADDRINUSE.
+
   - Configuration options for logfiles now accept mixed case reserved 
     words (e.g. "AUTO" or "auto") for consistency with other options.
 

+ 13 - 2
src/supervisor/options.py

@@ -1000,15 +1000,22 @@ class ServerOptions(Options):
         self.signal = sig
 
     def openhttpservers(self, supervisord):
-        from supervisor.http import make_http_servers
         try:
-            self.httpservers = make_http_servers(self, supervisord)
+            self.httpservers = self.make_http_servers(supervisord)
         except socket.error, why:
             if why[0] == errno.EADDRINUSE:
                 self.usage('Another program is already listening on '
                            'a port that one of our HTTP servers is '
                            'configured to use.  Shut this program '
                            'down first before starting supervisord.')
+            else:
+                help = 'Cannot open an HTTP server: socket.error reported'
+                errorname = errno.errorcode.get(why[0])
+                if errorname is None:
+                    self.usage('%s %s' % (help, why[0]))
+                else:
+                    self.usage('%s errno.%s (%d)' % 
+                               (help, errorname, why[0]))
             self.unlink_socketfiles = False
         except ValueError, why:
             self.usage(why[0])
@@ -1195,6 +1202,10 @@ class ServerOptions(Options):
         for msg in info_messages:
             self.logger.info(msg)
 
+    def make_http_servers(self, supervisord):
+        from supervisor.http import make_http_servers
+        return make_http_servers(self, supervisord)
+
     def close_fd(self, fd):
         try:
             os.close(fd)

+ 90 - 0
src/supervisor/tests/test_options.py

@@ -7,7 +7,9 @@ import socket
 import unittest
 import signal
 import shutil
+import errno
 
+from supervisor.tests.base import DummySupervisor
 from supervisor.tests.base import DummyLogger
 from supervisor.tests.base import DummyOptions
 from supervisor.tests.base import DummyPConfig
@@ -885,6 +887,94 @@ class ServerOptionsTests(unittest.TestCase):
         instance.clear_autochildlogdir()
         self.assertEqual(instance.logger.data, ['Could not clear childlog dir'])
 
+    def test_openhttpservers_reports_friendly_usage_when_eaddrinuse(self):
+        supervisord = DummySupervisor()
+        instance = self._makeOne()
+
+        def raise_eaddrinuse(supervisord):
+            raise socket.error(errno.EADDRINUSE)
+        instance.make_http_servers = raise_eaddrinuse
+
+        recorder = []
+        def record_usage(message):
+            recorder.append(message)
+        instance.usage = record_usage
+
+        instance.openhttpservers(supervisord)
+        self.assertEqual(len(recorder), 1)
+        expected = 'Another program is already listening'
+        self.assertTrue(recorder[0].startswith(expected))
+
+    def test_openhttpservers_reports_socket_error_with_errno(self):
+        supervisord = DummySupervisor()
+        instance = self._makeOne()
+        
+        def make_http_servers(supervisord):
+            raise socket.error(errno.EPERM)
+        instance.make_http_servers = make_http_servers        
+
+        recorder = []
+        def record_usage(message):
+            recorder.append(message)
+        instance.usage = record_usage
+        
+        instance.openhttpservers(supervisord)
+        self.assertEqual(len(recorder), 1)
+        expected = ('Cannot open an HTTP server: socket.error '
+                    'reported errno.EPERM (%d)' % errno.EPERM)
+        self.assertEqual(recorder[0], expected)
+
+    def test_openhttpservers_reports_other_socket_errors(self):
+        supervisord = DummySupervisor()
+        instance = self._makeOne()
+        
+        def make_http_servers(supervisord):
+            raise socket.error('uh oh')
+        instance.make_http_servers = make_http_servers            
+
+        recorder = []
+        def record_usage(message):
+            recorder.append(message)
+        instance.usage = record_usage
+        
+        instance.openhttpservers(supervisord)
+        self.assertEqual(len(recorder), 1)
+        expected = ('Cannot open an HTTP server: socket.error '
+                    'reported uh oh')
+        self.assertEqual(recorder[0], expected)                
+
+    def test_openhttpservers_reports_value_errors(self):
+        supervisord = DummySupervisor()
+        instance = self._makeOne()
+        
+        def make_http_servers(supervisord):
+            raise ValueError('not prefixed with help')
+        instance.make_http_servers = make_http_servers
+        
+        recorder = []
+        def record_usage(message):
+            recorder.append(message)
+        instance.usage = record_usage
+        
+        instance.openhttpservers(supervisord)
+        self.assertEqual(len(recorder), 1)
+        expected = 'not prefixed with help'
+        self.assertEqual(recorder[0], expected)                
+
+    def test_openhttpservers_does_not_catch_other_exception_types(self):
+        supervisord = DummySupervisor()
+        instance = self._makeOne()
+        
+        def make_http_servers(supervisord):
+            raise OverflowError
+        instance.make_http_servers = make_http_servers
+
+        # this scenario probably means a bug in supervisor.  we dump
+        # all the gory details on the poor user for troubleshooting
+        self.assertRaises(OverflowError,
+                          instance.openhttpservers, supervisord)
+
+
 class TestProcessConfig(unittest.TestCase):
     def _getTargetClass(self):
         from supervisor.options import ProcessConfig