1
1
#!/usr/bin/env python
2
2
3
- """A script for starting filetracker server using lighttpd ."""
3
+ """A script for starting filetracker server using gunicorn ."""
4
4
5
5
from __future__ import absolute_import
6
+
7
+ import multiprocessing
6
8
import os
7
- import os .path
9
+ import re
10
+ import signal
11
+ import subprocess
8
12
import sys
9
- from optparse import OptionParser
10
13
import tempfile
11
- import subprocess
12
- import signal
14
+ from optparse import OptionParser
13
15
14
- import filetracker .servers .files
15
- import filetracker .servers .base
16
+ from filetracker .servers .files import FiletrackerServer
16
17
from filetracker .servers .migration import MigrationFiletrackerServer
17
18
18
19
# Clients may use this as a sensible default port to connect to.
19
20
DEFAULT_PORT = 9999
20
21
21
22
23
+ def strip_margin (text ):
24
+ return re .sub ('\n [ \t ]*\|' , '\n ' , text )
25
+
26
+
22
27
def main (args = None ):
23
- epilog = "If LIGHTTPD_DIR is set in environment, it is assumed that " \
24
- "the lighttpd binary resides in that directory together with " \
25
- "the required modules: mod_fastcgi, mod_setenv and mod_status."
26
- parser = OptionParser (epilog = epilog )
28
+ parser = OptionParser ()
27
29
parser .add_option ('-p' , '--port' , dest = 'port' , default = DEFAULT_PORT ,
28
- type = " int" ,
30
+ type = ' int' ,
29
31
help = "Listen on specified port number" )
30
32
parser .add_option ('-l' , '--listen-on' , dest = 'listen_on' ,
31
33
default = '127.0.0.1' ,
@@ -34,17 +36,19 @@ def main(args=None):
34
36
help = "Specify Filetracker dir (taken from FILETRACKER_DIR "
35
37
"environment variable if not present)" )
36
38
parser .add_option ('-L' , '--log' , dest = 'log' , default = None ,
37
- help = "Log file location (no log by default)" )
39
+ help = "Error log file location (no log by default)" )
40
+ parser .add_option ('--access-log' , dest = 'access_log' , default = None ,
41
+ help = "Access log file location (no log by default)" )
38
42
parser .add_option ('-D' , '--no-daemon' , dest = 'daemonize' ,
39
43
action = 'store_false' , default = True ,
40
44
help = "Do not daemonize, stay in foreground" )
41
- parser .add_option ('--lighttpd-bin' , dest = 'lighttpd_bin' ,
42
- default = 'lighttpd' ,
43
- help = "Specify the lighttpd binary to use" )
44
45
parser .add_option ('--fallback-url' , dest = 'fallback_url' ,
45
46
default = None ,
46
47
help = "Turns on migration mode "
47
48
"and redirects requests to nonexistent files to the remote" )
49
+ parser .add_option ('--workers' , dest = 'workers' , type = 'int' ,
50
+ default = 2 * multiprocessing .cpu_count (),
51
+ help = "Specifies the amount of worker processes to use" )
48
52
options , args = parser .parse_args (args )
49
53
if args :
50
54
parser .error ("Unrecognized arguments: " + ' ' .join (args ))
@@ -59,80 +63,51 @@ def main(args=None):
59
63
if not os .path .exists (docroot ):
60
64
os .makedirs (docroot , 0o700 )
61
65
62
- if options .fallback_url is not None :
63
- run_migration_server (options )
64
- os .exit (0 )
65
-
66
- LIGHTHTTPD_CONF = """
67
- server.tag = "filetracker"
68
- server.document-root = "%(docroot)s"
69
- server.port = %(port)d
70
- server.bind = "%(listen_on)s"
71
- server.modules = ( "mod_fastcgi", "mod_status", "mod_setenv" )
72
- status.status-url = "/status"
73
- #debug.log-response-header = "enable"
74
- #debug.log-request-header = "enable"
75
- #debug.log-request-handling = "enable"
76
- #debug.log-condition-handling = "enable"
77
- fastcgi.debug = 1
78
- mimetype.assign = (
79
- "" => "application/octet-stream"
80
- )
81
- fastcgi.server += (
82
- "" =>
83
- (( "bin-path" => "%(interpreter)s %(files_script)s",
84
- "bin-environment" => (
85
- "FILETRACKER_DIR" => "%(filetracker_dir)s"
86
- ),
87
- "socket" => "%(tempdir)s/filetracker-files.%(pid)d",
88
- "check-local" => "disable",
89
- "fix-root-scriptname" => "enable"
90
- ))
91
- )
92
- """ % dict (
93
- filetracker_dir = filetracker_dir ,
94
- docroot = docroot ,
95
- port = options .port ,
96
- listen_on = options .listen_on ,
97
- interpreter = sys .executable ,
98
- files_script = filetracker .servers .files .__file__ ,
99
- pid = os .getpid (),
100
- tempdir = tempfile .gettempdir ())
66
+ gunicorn_settings = strip_margin ("""
67
+ |bind = ['{listen_on}:{port}']
68
+ |daemon = {daemonize}
69
+ |workers = {workers}
70
+ |raw_env = ['FILETRACKER_DIR={filetracker_dir}',
71
+ | 'FILETRACKER_FALLBACK_URL={fallback_url}']
72
+ """ .format (
73
+ listen_on = options .listen_on ,
74
+ port = options .port ,
75
+ daemonize = options .daemonize ,
76
+ workers = options .workers ,
77
+ filetracker_dir = options .dir ,
78
+ fallback_url = options .fallback_url
79
+ ))
101
80
102
81
if options .log :
103
- LIGHTHTTPD_CONF += """
104
- server.modules += ( "mod_accesslog" )
105
- accesslog.filename = "%(log)s"
106
- """ % dict (log = os .path .abspath (options .log ))
82
+ gunicorn_settings += strip_margin ("""
83
+ |errorlog = '{errorlog}'
84
+ |capture_output = True
85
+ """ .format (
86
+ errorlog = options .log ,
87
+ ))
88
+ if options .access_log :
89
+ gunicorn_settings += strip_margin ("""
90
+ |accesslog = '{accesslog}'
91
+ """ .format (
92
+ accesslog = options .access_log ,
93
+ ))
107
94
108
95
conf_fd , conf_path = tempfile .mkstemp (text = True )
109
96
try :
110
97
conf_file = os .fdopen (conf_fd , 'w' )
111
- conf_file .write (LIGHTHTTPD_CONF )
98
+ conf_file .write (gunicorn_settings )
112
99
conf_file .close ()
113
100
114
- env = os .environ .copy ()
115
- if sys .platform == 'darwin' or not options .daemonize :
116
- # setsid(1) is not available on Mac
117
- args = []
118
- else :
119
- args = ['setsid' ]
120
- if 'LIGHTTPD_DIR' in os .environ :
121
- server_dir = os .environ ['LIGHTTPD_DIR' ]
122
- args += [os .path .join (server_dir , 'lighttpd' ),
123
- '-f' , conf_path , '-m' , server_dir ]
124
- env ['LD_LIBRARY_PATH' ] = server_dir + ':' \
125
- + env .get ('LD_LIBRARY_PATH' , '' )
101
+ args = ['gunicorn' , '-c' , conf_path ]
102
+ if options .fallback_url is not None :
103
+ args .append ('filetracker.servers.run:gunicorn_entry_migration' )
126
104
else :
127
- args += [options .lighttpd_bin , '-f' , conf_path ]
128
-
129
- if not options .daemonize :
130
- args .append ('-D' )
105
+ args .append ('filetracker.servers.run:gunicorn_entry' )
131
106
132
107
try :
133
- popen = subprocess .Popen (args , env = env )
108
+ popen = subprocess .Popen (args )
134
109
except OSError as e :
135
- raise RuntimeError ("Cannot run lighttpd :\n %s" % e )
110
+ raise RuntimeError ("Cannot run gunicorn :\n %s" % e )
136
111
137
112
signal .signal (signal .SIGINT , lambda signum , frame : popen .terminate ())
138
113
signal .signal (signal .SIGTERM , lambda signum , frame : popen .terminate ())
@@ -141,16 +116,36 @@ def main(args=None):
141
116
if not options .daemonize :
142
117
sys .exit (retval )
143
118
if retval :
144
- raise RuntimeError ("Lighttpd exited with code %d" % retval )
119
+ raise RuntimeError ("gunicorn exited with code %d" % retval )
145
120
finally :
146
- # At this point lighttpd does not need the configuration file, so it
121
+ # At this point gunicorn does not need the configuration file, so it
147
122
# can be safely deleted.
148
123
os .unlink (conf_path )
149
124
150
125
151
- def run_migration_server (options ):
152
- server = MigrationFiletrackerServer (options .fallback_url , options .dir )
153
- filetracker .servers .base .start_standalone (server , options .port )
126
+ # This filetracker_instance is cached between requests within one WSGI process.
127
+ # There are no threading problems though,
128
+ # because each process is set to use 1 thread.
129
+ filetracker_instance = None
130
+
131
+
132
+ def gunicorn_entry (env , start_response ):
133
+ global filetracker_instance
134
+ if filetracker_instance is None :
135
+ filetracker_instance = FiletrackerServer ()
136
+ return filetracker_instance (env , start_response )
137
+
138
+
139
+ def gunicorn_entry_migration (env , start_response ):
140
+ global filetracker_instance
141
+ if filetracker_instance is None :
142
+ fallback = os .environ .get ('FILETRACKER_FALLBACK_URL' , None )
143
+ if not fallback :
144
+ raise RuntimeError ("Configuration error. Fallback url not set." )
145
+ filetracker_instance = MigrationFiletrackerServer (
146
+ redirect_url = fallback
147
+ )
148
+ return filetracker_instance (env , start_response )
154
149
155
150
156
151
if __name__ == '__main__' :
0 commit comments