def main(): import optparse parser = optparse.OptionParser( usage="usage: %prog [options]", description="Simple saranwrap.Server wrapper") parser.add_option( '-c', '--child', default=False, action='store_true', help='Wrap an object serialed via setattr.') parser.add_option( '-m', '--module', type='string', dest='module', default=None, help='a module to load and export.') parser.add_option( '-l', '--logfile', type='string', dest='logfile', default=None, help='file to log to.') options, args = parser.parse_args() global _g_logfile if options.logfile: _g_logfile = open(options.logfile, 'a') if options.module: export = api.named(options.module) server = Server(sys.stdin, sys.stdout, export) elif options.child: server = Server(sys.stdin, sys.stdout, {}) # *HACK: some modules may emit on stderr, which breaks everything. class NullSTDOut(object): def write(a, b): pass sys.stderr = NullSTDOut() sys.stdout = NullSTDOut() # Loop until EOF server.loop() if _g_logfile: _g_logfile.close()
def config_factory(args): args["app_factory"] = "spawning.wsgi_factory.app_factory" args["app"] = args["args"][0] args["middleware"] = args["args"][1:] args["source_directories"] = [os.path.split(inspect.getfile(inspect.getmodule(api.named(args["app"]))))[0]] return args
def __init__(self, sock, factory, args, **kwargs): self.sock = sock self.factory = factory self.config = api.named(factory)(args) self.args = args self.child_pipes = {} self.log = logging.getLogger('Spawning') if not kwargs.get('log_handler'): self.log.addHandler(logging.StreamHandler()) self.log.setLevel(logging.DEBUG) self.controller_pid = os.getpid() self.num_processes = int(self.config.get('num_processes', 0))
def config_factory(args): args['django_settings_module'] = args.get('args', [None])[0] args['app_factory'] = 'spawning.django_factory.app_factory' ## TODO More directories ## INSTALLED_APPS (list of quals) ## ROOT_URL_CONF (qual) ## MIDDLEWARE_CLASSES (list of quals) ## TEMPLATE_CONTEXT_PROCESSORS (list of quals) settings_module = api.named(args['django_settings_module']) dirs = [os.path.split( inspect.getfile( inspect.getmodule( settings_module)))[0]] args['source_directories'] = dirs return args
def main(): parser = optparse.OptionParser() parser.add_option("-r", "--reload", action='store_true', dest='reload', help='If --reload is passed, reload the server any time ' 'a loaded module changes.') options, args = parser.parse_args() if len(args) != 5: print "Usage: %s controller_pid httpd_fd death_fd factory_qual factory_args" % ( sys.argv[0], ) sys.exit(1) controller_pid, httpd_fd, death_fd, factory_qual, factory_args = args controller_pid = int(controller_pid) config = api.named(factory_qual)(json.loads(factory_args)) setproctitle("spawn: child (%s)" % ", ".join(config.get("args"))) ## Set up the reloader if options.reload: watch = config.get('watch', None) if watch: watching = ' and %s' % watch else: watching = '' print "(%s) reloader watching sys.modules%s" % (os.getpid(), watching) api.spawn( reloader_dev.watch_forever, controller_pid, 1, watch) ## The parent will catch sigint and tell us to shut down signal.signal(signal.SIGINT, signal.SIG_IGN) api.spawn(read_pipe_and_die, int(death_fd), api.getcurrent()) ## Make the socket object from the fd given to us by the controller sock = greenio.GreenSocket( socket.fromfd(int(httpd_fd), socket.AF_INET, socket.SOCK_STREAM)) serve_from_child( sock, config, controller_pid)
def test_named(self): named_foo = api.named('tests.api_test.Foo') self.assertEqual(named_foo.__name__, "Foo")
def serve_from_child(sock, config, controller_pid): threads = config.get('threadpool_workers', 0) wsgi_application = api.named(config['app_factory'])(config) if config.get('coverage'): wsgi_application = FigleafCoverage(wsgi_application) if threads > 1: wsgi_application = ExecuteInThreadpool(wsgi_application) elif threads != 1: print "(%s) not using threads, installing eventlet cooperation monkeypatching" % ( os.getpid(), ) from eventlet import util util.wrap_socket_with_coroutine_socket() #util.wrap_pipes_with_coroutine_pipes() #util.wrap_threading_local_with_coro_local() host, port = sock.getsockname() access_log_file = config.get('access_log_file') if access_log_file is not None: access_log_file = open(access_log_file, 'a') max_age = 0 if config.get('max_age'): max_age = int(config.get('max_age')) server_event = coros.event() http_version = config.get('no_keepalive') and 'HTTP/1.0' or 'HTTP/1.1' try: wsgi_args = (sock, wsgi_application) wsgi_kwargs = {'log' : access_log_file, 'server_event' : server_event, 'max_http_version' : http_version} if config.get('no_keepalive'): wsgi_kwargs.update({'keepalive' : False}) if max_age: wsgi_kwargs.update({'timeout_value' : True}) api.with_timeout(max_age, wsgi.server, *wsgi_args, **wsgi_kwargs) else: wsgi.server(*wsgi_args, **wsgi_kwargs) except KeyboardInterrupt: pass except ExitChild: pass ## Set a deadman timer to violently kill the process if it doesn't die after ## some long timeout. signal.signal(signal.SIGALRM, deadman_timeout) signal.alarm(config['deadman_timeout']) ## Once we get here, we just need to handle outstanding sockets, not ## accept any new sockets, so we should close the server socket. sock.close() server = server_event.wait() last_outstanding = None if server.outstanding_requests: ## Let's tell our parent that we're dying try: os.kill(controller_pid, signal.SIGUSR1) except OSError, e: if not e.errno == errno.ESRCH: raise
def app_factory(config): app = api.named(config["app"]) for mid in config["middleware"]: app = api.named(mid)(app) return app
def run_controller(factory_qual, args, sock=None): controller_pid = os.getpid() print "(%s) **** Controller starting up at %s" % ( controller_pid, time.asctime()) try: config = api.named(factory_qual)(args) except: print_exc("Couldn't import the WSGI factory! Panic!") restart_controller(factory_qual, args, sock, panic=True) ## Never gets here! pidfile = config.get("pidfile") if pidfile: f = open(pidfile, "w") try: f.write("%s\n" % (controller_pid,)) finally: f.close() setproctitle("spawn: controller " + args["argv_str"]) dev = config.get('dev', False) if not dev: ## Set up the production reloader that watches the svn revision number. if not os.fork(): if sock is not None: sock.close() base = os.path.dirname(__file__) os.chdir(base) args = [ sys.executable, 'reloader_svn.py', '--pid=' + str(controller_pid), '--dir=' + base, ] for dirname in config.get('source_directories', []): args.append('--dir=' + dirname) os.execve(sys.executable, args, environ()) ## Never gets here! if sock is None: sock = bind_socket(config) spawn_new_children(sock, factory_qual, args, config) start_time = time.time() start_delay = args.get('start_delay') while True: reap_children() ## Random heuristic: If we've been running for 64x longer than the start_delay ## or 5 minutes, whatever is shorter, we can clear the start_delay if start_delay is not None: if time.time() - start_time > min(start_delay * 64, 60 * 5): print "(%s) We've been running OK for a while, clear the exponential backoff" % ( os.getpid(), ) del args['start_delay'] if RESTART_CONTROLLER: break if KEEP_GOING: restart_controller(factory_qual, args, sock, panic=PANIC)