/silverlining/commands/serve.py
Python | 167 lines | 142 code | 13 blank | 12 comment | 31 complexity | 1bd81b0df028322b818665cd76dae349 MD5 | raw file
Possible License(s): GPL-2.0
- """Serve an app locally/for development"""
- import os
- import sys
- import subprocess
- import glob
- import shlex
- import urlparse
- import urllib
- from cmdutils import CommandError
- from tempita import Template
- from paste import httpserver
- import silversupport
- from silversupport.appconfig import AppConfig
- from silversupport.shell import run
- def command_serve(config):
- dir = os.path.abspath(config.args.dir)
- if not os.path.exists(os.path.join(dir, 'app.ini')):
- raise CommandError(
- "Could not find app.ini in %s" % config.args.dir)
- appconfig = AppConfig(os.path.join(dir, 'app.ini'))
- appconfig.check_service_setup(config.logger)
- if appconfig.platform == 'python':
- serve_python(config, appconfig)
- elif appconfig.platform == 'php':
- serve_php(config, appconfig)
- def serve_python(config, appconfig):
- dir = os.path.abspath(config.args.dir)
- if os.path.exists(os.path.join(dir, 'bin', 'python')):
- # We are in a virtualenv situation...
- cmd = [os.path.join(dir, 'bin', 'python'),
- os.path.abspath(os.path.join(__file__, '../../devel-runner.py')),
- dir]
- else:
- cmd = [sys.executable,
- os.path.abspath(os.path.join(__file__, '../../devel-runner.py')),
- dir]
- ## FIXME: should cut down the environ significantly
- environ = os.environ.copy()
- environ['SILVER_INSTANCE_NAME'] = 'localhost'
- environ['SILVER_PASTE_LOCATION'] = httpserver.__file__
- environ['SILVER_SERVE_HOST'] = config.args.host
- environ['SILVER_SERVE_PORT'] = config.args.port
- if config.args.config:
- environ['SILVER_APP_CONFIG'] = os.path.abspath(config.args.config)
- proc = None
- try:
- try:
- while 1:
- try:
- proc = subprocess.Popen(cmd, cwd=dir, env=environ)
- except:
- config.logger.warn('Error running command: %s' % ' '.join(cmd))
- raise
- proc.communicate()
- if proc.returncode == 3:
- # Signal to do a restart
- config.logger.notify('Restarting...')
- else:
- return
- sys.exit(proc.returncode)
- finally:
- if (proc is not None
- and hasattr(os, 'kill')):
- import signal
- try:
- os.kill(proc.pid, signal.SIGTERM)
- except (OSError, IOError):
- pass
- except KeyboardInterrupt:
- print 'Terminating'
- def serve_php(config, appconfig):
- apache_config_tmpl = Template.from_filename(
- os.path.join(os.path.dirname(__file__), 'php-devel-server.conf.tmpl'))
- path_prefixes = [os.path.join(appconfig.app_dir, 'static'),
- appconfig.php_root]
- if appconfig.writable_root_location != '/dev/null':
- path_prefixes.append(appconfig.writable_root_location
- + '/%{ENV:SILVER_HOSTNAME}')
- path_prefixes.append(appconfig.writable_root_location)
- tempdir = os.path.join(os.path.abspath(config.args.dir), '.apache')
- if not os.path.exists(tempdir):
- os.makedirs(tempdir)
- includes = glob.glob('/etc/apache2/mods-enabled/*.load')
- includes += glob.glob('/etc/apache2/mods-enabled/*.conf')
- silver_secret_file = os.path.join(tempdir, 'secret.txt')
- silver_env_vars = os.path.join(tempdir, 'silver-env-vars.php')
- appconfig.write_php_env(silver_env_vars)
- if not os.path.exists(silver_secret_file):
- fp = open(silver_secret_file, 'wb')
- fp.write('localsecret')
- fp.close()
- apache_config = apache_config_tmpl.substitute(
- mgr_scripts=os.path.join(os.path.dirname(os.path.dirname(__file__)), 'mgr-scripts'),
- path_prefixes=path_prefixes,
- ## FIXME: better values:
- tempdir=tempdir,
- apache_pid_file=os.path.join(tempdir, 'apache.pid'),
- mods_dir='/etc/apache2/mods-enabled',
- includes=includes,
- silver_instance_dir=os.path.abspath(config.args.dir),
- silver_secret_file=silver_secret_file,
- silver_env_vars=silver_env_vars,
- silver_funcs=os.path.join(os.path.dirname(silversupport.__file__), 'php', 'functions.php'),
- )
- conf_file = os.path.join(tempdir, 'apache.conf')
- config.logger.info('Writing config to %s' % conf_file)
- fp = open(conf_file, 'w')
- fp.write(apache_config)
- fp.close()
- exe_name = search_path(['apache2', 'apache', 'httpd'])
- config.logger.notify('Serving on http://localhost:8080')
- ## FIXME: -X would also be an alternative to -DFOREGROUND; not sure which is better
- ## FIXME: this logic (and call_script()) repeats stuff in devel-server.py:
- for url in appconfig.update_fetch:
- if url.startswith('script:'):
- script = url[len('script:'):]
- print 'Running update script %s' % script
- call_script(appconfig, script)
- else:
- print 'Fetching update URL %s' % url
- url = urlparse.urljoin('http://localhost:8080', url)
- r = urllib.urlopen(url)
- ## FIXME: handle non-200 status
- body = r.read()
- if body:
- sys.stdout.write(body)
- if not body.endswith('\n'):
- sys.stdout.write('\n')
- sys.stdout.flush()
- run([exe_name, '-f', conf_file,
- '-d', config.args.dir, '-DFOREGROUND'])
- def call_script(app_config, script):
- run([sys.executable, os.path.join(os.path.dirname(os.path.dirname(__file__)),
- 'mgr-scripts', 'call-script.py'),
- app_config.app_dir] + shlex.split(script))
- def _turn_sigterm_into_systemexit():
- """
- Attempts to turn a SIGTERM exception into a SystemExit exception.
- """
- try:
- import signal
- except ImportError:
- return
- def handle_term(signo, frame):
- raise SystemExit
- signal.signal(signal.SIGTERM, handle_term)
- def search_path(exe_names):
- ## FIXME: should I allow for some general environmental variable override here?
- paths = os.environ['PATH'].split(os.path.pathsep)
- for name in exe_names:
- for path in paths:
- if os.path.exists(os.path.join(path, name)):
- return name
- return exe_names[0]