Rev 42: Add proftpd. in http://bazaar.launchpad.net/%7Evila/bzr/local-test-server

Vincent Ladeuil v.ladeuil+lp at free.fr
Fri Jun 27 18:38:36 BST 2008


At http://bazaar.launchpad.net/%7Evila/bzr/local-test-server

------------------------------------------------------------
revno: 42
revision-id: v.ladeuil+lp at free.fr-20080627173832-usa6htm9m3x1ecm9
parent: v.ladeuil+lp at free.fr-20080626153537-jgg7h9xzs6n34xl1
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: local_test_server
timestamp: Fri 2008-06-27 19:38:32 +0200
message:
  Add proftpd.
  
  * tests/test_utils.py:
  (ServerAdapter.__init__): Add proftpd.
  
  * test_server.py:
  (ProftpdFeature): New feature.
  (Proftpd): New test server.
  (get_test_permutations): Inject proftpd.
  
  * configs/proftpd.conf: 
  First rough version of a working config for proftpd launched via
  sudo, which is bad at least because it may block bzr selftest :-/
  
  * server.py:
  (Server._wait_for_server_process_death): Generalize by adding a
  kill callable parameter.
  (Proftpd): New server.
  
  * config.py:
  (Config.expand): Catch errors during expansion, trivial
  implementation, need tests.
  (Proftpd): New config.
added:
  configs/proftpd.conf           proftpd.conf-20080627091409-x66ke05k4npbe4se-1
modified:
  TODO                           todo-20080526134120-eibvvebw74t8j2xh-1
  config.py                      __init__.py-20080523170713-0c8mnxio9r41iyhk-1
  server.py                      server.py-20080524160639-rqhbexqatjqbwypw-1
  test_server.py                 test_server.py-20080530070615-f555godexnk7frms-1
  tests/test_utils.py            test_utils.py-20080603162150-w01001l902j0gu95-1
-------------- next part --------------
=== modified file 'TODO'
--- a/TODO	2008-06-26 15:35:37 +0000
+++ b/TODO	2008-06-27 17:38:32 +0000
@@ -35,6 +35,15 @@
 
 ** ftp
 
+*** make the fyp servers issue a big fat warning to the users
+
+*** try to restrict access from locahost only as much as possible.
+
+*** better explain the strategy (auth required, hard or
+   impossible to run without being root, needs wide write access,
+   security implications, etc), mention Robert's virtual machine
+   idea with its implications.
+
 *** file bugs against conflicting ftp servers packages
 
 *** investigate dpkg --force-depends to be able to force the

=== modified file 'config.py'
--- a/config.py	2008-06-24 20:06:35 +0000
+++ b/config.py	2008-06-27 17:38:32 +0000
@@ -83,9 +83,12 @@
         :param infile: A file containing the keywords to be expanded
         :param outfile: A opened file where the expansion result is written
         """
-        for line in infile.readlines():
-            outfile.write(line % self.values)
-
+        for line_num, line in enumerate(infile.readlines()):
+            try:
+                outfile.write(line % self.values)
+            except ValueError, e:
+                # FIXME: This need tests (and a traceback is ugly)
+                raise SyntaxError('At line %d Error: %s' % (line_num + 1, e))
     def ensure_required_dirs_exist(self):
         for dir in self.required_dirs.keys():
             try:
@@ -160,3 +163,9 @@
     def __init__(self, _base_dir=None):
         super(Vsftpd, self).__init__('vsftpd', _base_dir=_base_dir)
 
+
+class Proftpd(Config):
+
+    def __init__(self, _base_dir=None):
+        super(Proftpd, self).__init__('proftpd', _base_dir=_base_dir)
+

=== added file 'configs/proftpd.conf'
--- a/configs/proftpd.conf	1970-01-01 00:00:00 +0000
+++ b/configs/proftpd.conf	2008-06-27 17:38:32 +0000
@@ -0,0 +1,60 @@
+#
+# Based on /etc/proftpd/proftpd.conf in Ubuntu Gusty
+#
+
+UseIPv6				off
+
+ServerName			"proftpd bzr test server"
+ServerType			standalone
+DeferWelcome			off
+
+MultilineRFC2228		on
+DefaultServer			on
+ShowSymlinks			on
+
+TimeoutNoTransfer		600
+TimeoutStalled			600
+TimeoutIdle			300
+
+ListOptions                	"-l"
+
+# DenyFilter			\*.*/
+
+Port                            %(port)s
+
+# FIXME: Surprisingly (chokingly ?) the default is ascii and bzrlib
+# doesn't force a binary mode ???
+DefaultTransferMode             binary
+
+# MaxInstances			30
+
+# Set the user and group that the server normally runs at.
+#User				%(user)s
+#Group				nogroup
+
+# Umask 022 is a good standard umask to prevent new files and dirs
+# (second parm) from being group and world writable.
+Umask				022  022
+
+PidFile     %(pid_file)s
+TransferLog %(var_log_dir)s/xferlog
+SystemLog   %(log_file)s
+
+DebugLevel 9
+
+# Restrict visibility to /tmp since proftpd will chroot to the
+# anonymous home dir.
+
+# FIXME: What if the user has set TMP ? Hmm, we're doomed I think.
+
+<Anonymous /tmp>
+   User              %(user)s
+   UserAlias         anonymous %(user)s
+   AllowOverwrite    on
+   AllowStoreRestart on
+   <Limit ALL>
+          AllowAll
+   </Limit>
+</Anonymous>
+
+# FIXME: Restrict access from localhost only

=== modified file 'server.py'
--- a/server.py	2008-06-26 15:35:37 +0000
+++ b/server.py	2008-06-27 17:38:32 +0000
@@ -189,6 +189,8 @@
         return pid
 
     def is_running(self):
+        # FIXME: That's not enough, the process may have died leaving the
+        # pid_file behind it.
         return bool(self.get_pid() is not None)
 
     # Sometimes we need to let some server (apache2 I'm looking at you) do its
@@ -277,16 +279,17 @@
                 raise
         return False
 
-    def _wait_for_server_process_death(self, sig=None):
+    def _wait_for_server_process_death(self, sig=None, kill=None):
         if sig is None:
             sig = signal.SIGTERM
-
+        if kill is None:
+            kill = self._kill_server
         def server_is_dead():
             pid = self.get_pid()
             if pid is None:
                 return True
             else:
-                killed = self._kill_server(pid, sig)
+                killed = kill(pid, sig)
                 return killed
 
         return self._hk_poll(server_is_dead, interval=500.0)
@@ -538,6 +541,54 @@
         osutils.delete_any(self.get_config_value('pid_file'))
 
 
+class Proftpd(Server):
+    """proftpd server.
+
+    There is no way to launch this server without being root. Yet we try to run
+    as root as little as possible, so stop and start use sudo.
+
+    See config.Proftpd and configs/proftpd.conf for more details.
+    """
+
+    _server_command_name = 'proftpd'
+
+    def __init__(self, port, _conf=None):
+        if _conf is None:
+            _conf = config.Proftpd()
+        # FIXME: it's weird to reverse parameter order
+        super(Proftpd, self).__init__(_conf, port)
+        self.output_config_path = self.config.abspath('etc/proftpd.conf')
+
+    def _start(self):
+        try:
+            subprocess.check_call(['sudo', self._server_command_name,
+                                   '-c', self.output_config_path,
+                                   # Collision checking requires root access
+                                   '--nocollision',])
+        except subprocess.CalledProcessError, e:
+            raise LTSCantStartError(self, extra=e)
+        if not self._wait_for_pidfile_to_be_created():
+            raise LTSCantStartError(
+                self, extra='Did not create pid file %s'
+                % self.get_config_value('pid_file'))
+
+    def _kill_server_via_sudo(self, pid, sig=None):
+        if sig is None:
+            sig = signal.SIGTERM
+        try:
+            subprocess.check_call(['sudo', 'kill', '-%s' % sig, '%s' % pid])
+        except subprocess.CalledProcessError, e:
+            raise LTSCantStopError(self, extra=e)
+        # Don't try yo be too smart, just rely on pid check to find out if the
+        # process is dead or not
+        return False
+
+    def _stop(self):
+        if not self._wait_for_server_process_death(
+            kill=self._kill_server_via_sudo):
+            raise LTSCantStopError(self)
+
+
 _next_available_port = 49000
 _max_available_port = 49151
 def _get_available_port():
@@ -553,6 +604,8 @@
     This function returns an available port in a range chosen in unassigned
     ones as described in http://www.iana.org/assignments/port-numbers
     """
+    # FIXME: Yeah, that's nice, but is the url above really implemented where
+    # *we* run ?
     global _next_available_port
     global _max_available_port
     port = _next_available_port
@@ -573,6 +626,7 @@
 _servers['lighttpd-dav'] = LighttpdDAV(37004)
 _servers['apache2-svn'] = Apache2SVN(37005)
 _servers['vsftpd'] = Vsftpd(37006)
+_servers['proftpd'] = Proftpd(37007)
 
 
 def get_server(name):

=== modified file 'test_server.py'
--- a/test_server.py	2008-06-25 18:36:46 +0000
+++ b/test_server.py	2008-06-27 17:38:32 +0000
@@ -95,6 +95,11 @@
     _server_name = 'vsftpd'
 
 
+class ProftpdFeature(LocalTestServerFeature):
+
+    _server_name = 'proftpd'
+
+
 class LocalTestServer(transport.Server):
 
     # Must be set by daughter classes
@@ -269,6 +274,24 @@
         return self._base_url + self._test_working_dir
 
 
+class Proftpd(LocalFTPTestServer):
+
+    _server_name = 'proftpd'
+
+    def _build_base_url(self):
+        # We use anonymous to avoid having to store the password of the user
+        # running the tests
+        return '%s://anonymous@%s:%s/' % (self._url_protocol,
+                                          self.host, self.port)
+    def get_url(self):
+        """See bzrlib.transport.Server.get_url."""
+        # Remote the leading /tmp
+        prefix = '/tmp/'
+        if not self._test_working_dir.startswith(prefix):
+            raise AssertionError('%s must start with %s'
+                                 % (self._test_working_dir, prefix))
+        return self._base_url + self._test_working_dir[len(prefix):]
+
 
 # We have registered a transport for the purpose of adding new servers in the
 # test permutations. Registering a transport makes our module appears in the
@@ -315,4 +338,7 @@
     if VsftpdFeature().available():
         permutations.append((FtpTransport, Vsftpd))
 
+    if ProftpdFeature().available():
+        permutations.append((FtpTransport, Proftpd))
+
     return permutations

=== modified file 'tests/test_utils.py'
--- a/tests/test_utils.py	2008-06-26 15:35:37 +0000
+++ b/tests/test_utils.py	2008-06-27 17:38:32 +0000
@@ -119,6 +119,13 @@
                     _server_feature_class=test_server.VsftpdFeature,
                     _test_server_class=test_server.Vsftpd,
                     )),
+            ('proftpd', dict(
+                    _server_name='proftpd',
+                    _config_class=config.Proftpd,
+                    _server_class=server.Proftpd,
+                    _server_feature_class=test_server.ProftpdFeature,
+                    _test_server_class=test_server.Proftpd,
+                    )),
             ]
         self.scenarios = server_scenarios
 



More information about the bazaar-commits mailing list