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