浏览代码

Add sendProcessStdin method to rpc interface (cadged from etp_supervisor).

Chris McDonough 18 年之前
父节点
当前提交
562d17aec7
共有 4 个文件被更改,包括 90 次插入0 次删除
  1. 30 0
      src/supervisor/rpcinterface.py
  2. 4 0
      src/supervisor/tests/base.py
  3. 55 0
      src/supervisor/tests/test_rpcinterfaces.py
  4. 1 0
      src/supervisor/xmlrpc.py

+ 30 - 0
src/supervisor/rpcinterface.py

@@ -646,6 +646,36 @@ class SupervisorNamespaceRPCInterface:
         clearall.rpcinterface = self
         return clearall # deferred
 
+    def sendProcessStdin(self, name, chars):
+        """ Send a string of chars to the stdin of the process name.
+        If non-7-bit data is sent (unicode), it is encoded to utf-8 before
+        being sent to the process' stdin.  If chars is not a string
+        or is not unicode, raise INCORRECT_PARAMETERS.
+
+        @param string name        The process name to send to (or 'group:name')
+        @param string chars       The character data to send to the process
+        @return boolean result    Always return True unless error
+        """
+        self._update('sendProcessStdin')
+
+        if isinstance(chars, unicode):
+            chars = chars.encode('utf-8')
+
+        if not isinstance(chars, basestring):
+            raise RPCError(Faults.INCORRECT_PARAMETERS, chars)
+
+        group, process = self._getGroupAndProcess(name)
+
+        if process is None:
+            raise RPCError(Faults.BAD_NAME, name)
+
+        if not process.pid or process.killing:
+            raise RPCError(Faults.ALREADY_TERMINATED, name)
+
+        process.write(chars)
+
+        return True
+
 # this is not used in code but referenced via an entry point in the conf file
 def make_main_rpcinterface(supervisord):
     return SupervisorNamespaceRPCInterface(supervisord)

+ 4 - 0
src/supervisor/tests/base.py

@@ -309,6 +309,7 @@ class DummyProcess:
         self.stderr_buffer = ''
         self.stdout_logged = ''
         self.stderr_logged = ''
+        self.stdin_buffer = ''
         self.pipes = {}
         self.rpipes = {}
         self.dispatchers = {}
@@ -376,6 +377,9 @@ class DummyProcess:
     def drain_input_fd(self, fd):
         self.input_fd_drained = fd
 
+    def write(self, chars):
+        self.stdin_buffer += chars
+
 class DummyPConfig:
     def __init__(self, options, name, command, priority=999, autostart=True,
                  autorestart=True, startsecs=10, startretries=999,

+ 55 - 0
src/supervisor/tests/test_rpcinterfaces.py

@@ -923,6 +923,61 @@ class SupervisorNamespaceXMLRPCInterfaceTests(TestBase):
                           'status':xmlrpc.Faults.SUCCESS,
                           'description':'OK'})
 
+    def test_sendProcessStdin_raises_incorrect_params_when_not_chars(self):
+        options = DummyOptions()
+        pconfig1 = DummyPConfig(options, 'process1', 'foo')
+        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1)
+        interface   = self._makeOne(supervisord)
+        thing_not_chars = 42
+        from supervisor import xmlrpc
+        self._assertRPCError(xmlrpc.Faults.INCORRECT_PARAMETERS,
+                             interface.sendProcessStdin,
+                             'process1', thing_not_chars)
+    
+    def test_sendProcessStdin_raises_bad_name_when_no_process(self):
+        options = DummyOptions()
+        supervisord = PopulatedDummySupervisor(options, 'foo')
+        interface = self._makeOne(supervisord)
+        from supervisor import xmlrpc
+        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
+                             interface.sendProcessStdin,
+                             'nonexistant_process_name', 'chars for stdin')
+
+    def test_sendProcessStdin_raises_already_termed_when_not_process_pid(self):
+        options = DummyOptions()
+        pconfig1 = DummyPConfig(options, 'process1', 'foo')
+        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
+        supervisord.set_procattr('process1', 'pid', 0)
+        interface = self._makeOne(supervisord)
+        from supervisor import xmlrpc
+        self._assertRPCError(xmlrpc.Faults.ALREADY_TERMINATED,
+                            interface.sendProcessStdin,
+                            'process1', 'chars for stdin')
+
+    def test_sendProcessStdin_raises_already_termed_when_killing(self):
+        options = DummyOptions()
+        pconfig1 = DummyPConfig(options, 'process1', 'foo')
+        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
+        supervisord.set_procattr('process1', 'pid', 42)
+        supervisord.set_procattr('process1', 'killing',True)
+        interface   = self._makeOne(supervisord)
+        from supervisor import xmlrpc
+        self._assertRPCError(xmlrpc.Faults.ALREADY_TERMINATED,
+                             interface.sendProcessStdin,
+                             'process1', 'chars for stdin')
+        
+    def test_sendProcessStdin_writes_chars_and_returns_true(self):
+        options = DummyOptions()
+        pconfig1 = DummyPConfig(options, 'process1', 'foo')
+        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
+        supervisord.set_procattr('process1', 'pid', 42)
+        interface   = self._makeOne(supervisord)
+        chars = 'chars for stdin'
+        self.assertTrue(interface.sendProcessStdin('process1', chars))
+        self.assertEqual('sendProcessStdin', interface.update_text)
+        process1 = supervisord.process_groups['process1'].processes['process1']
+        self.assertEqual(process1.stdin_buffer, chars)
+
 class SystemNamespaceXMLRPCInterfaceTests(TestBase):
     def _getTargetClass(self):
         from supervisor import xmlrpc

+ 1 - 0
src/supervisor/xmlrpc.py

@@ -42,6 +42,7 @@ class Faults:
     NOT_RUNNING = 70
     SUCCESS = 80
     TIMED_OUT = 90
+    ALREADY_TERMINATED = 200
 
 def getFaultDescription(code):
     for faultname in Faults.__dict__: