PageRenderTime 20ms CodeModel.GetById 1ms app.highlight 14ms RepoModel.GetById 2ms app.codeStats 0ms

/silverlining/commands/serve.py

https://bitbucket.org/ianb/silverlining/
Python | 167 lines | 142 code | 13 blank | 12 comment | 26 complexity | 1bd81b0df028322b818665cd76dae349 MD5 | raw file
  1"""Serve an app locally/for development"""
  2import os
  3import sys
  4import subprocess
  5import glob
  6import shlex
  7import urlparse
  8import urllib
  9from cmdutils import CommandError
 10from tempita import Template
 11from paste import httpserver
 12import silversupport
 13from silversupport.appconfig import AppConfig
 14from silversupport.shell import run
 15
 16
 17def command_serve(config):
 18    dir = os.path.abspath(config.args.dir)
 19    if not os.path.exists(os.path.join(dir, 'app.ini')):
 20        raise CommandError(
 21            "Could not find app.ini in %s" % config.args.dir)
 22    appconfig = AppConfig(os.path.join(dir, 'app.ini'))
 23    appconfig.check_service_setup(config.logger)
 24    if appconfig.platform == 'python':
 25        serve_python(config, appconfig)
 26    elif appconfig.platform == 'php':
 27        serve_php(config, appconfig)
 28
 29
 30def serve_python(config, appconfig):
 31    dir = os.path.abspath(config.args.dir)
 32    if os.path.exists(os.path.join(dir, 'bin', 'python')):
 33        # We are in a virtualenv situation...
 34        cmd = [os.path.join(dir, 'bin', 'python'),
 35               os.path.abspath(os.path.join(__file__, '../../devel-runner.py')),
 36               dir]
 37    else:
 38        cmd = [sys.executable,
 39               os.path.abspath(os.path.join(__file__, '../../devel-runner.py')),
 40               dir]
 41    ## FIXME: should cut down the environ significantly
 42    environ = os.environ.copy()
 43    environ['SILVER_INSTANCE_NAME'] = 'localhost'
 44    environ['SILVER_PASTE_LOCATION'] = httpserver.__file__
 45    environ['SILVER_SERVE_HOST'] = config.args.host
 46    environ['SILVER_SERVE_PORT'] = config.args.port
 47    if config.args.config:
 48        environ['SILVER_APP_CONFIG'] = os.path.abspath(config.args.config)
 49    proc = None
 50    try:
 51        try:
 52            while 1:
 53                try:
 54                    proc = subprocess.Popen(cmd, cwd=dir, env=environ)
 55                except:
 56                    config.logger.warn('Error running command: %s' % ' '.join(cmd))
 57                    raise
 58                proc.communicate()
 59                if proc.returncode == 3:
 60                    # Signal to do a restart
 61                    config.logger.notify('Restarting...')
 62                else:
 63                    return
 64            sys.exit(proc.returncode)
 65        finally:
 66            if (proc is not None
 67                and hasattr(os, 'kill')):
 68                import signal
 69                try:
 70                    os.kill(proc.pid, signal.SIGTERM)
 71                except (OSError, IOError):
 72                    pass
 73    except KeyboardInterrupt:
 74        print 'Terminating'
 75
 76
 77def serve_php(config, appconfig):
 78    apache_config_tmpl = Template.from_filename(
 79        os.path.join(os.path.dirname(__file__), 'php-devel-server.conf.tmpl'))
 80    path_prefixes = [os.path.join(appconfig.app_dir, 'static'),
 81                     appconfig.php_root]
 82    if appconfig.writable_root_location != '/dev/null':
 83        path_prefixes.append(appconfig.writable_root_location
 84                             + '/%{ENV:SILVER_HOSTNAME}')
 85        path_prefixes.append(appconfig.writable_root_location)
 86    tempdir = os.path.join(os.path.abspath(config.args.dir), '.apache')
 87    if not os.path.exists(tempdir):
 88        os.makedirs(tempdir)
 89    includes = glob.glob('/etc/apache2/mods-enabled/*.load')
 90    includes += glob.glob('/etc/apache2/mods-enabled/*.conf')
 91    silver_secret_file = os.path.join(tempdir, 'secret.txt')
 92    silver_env_vars = os.path.join(tempdir, 'silver-env-vars.php')
 93    appconfig.write_php_env(silver_env_vars)
 94    if not os.path.exists(silver_secret_file):
 95        fp = open(silver_secret_file, 'wb')
 96        fp.write('localsecret')
 97        fp.close()
 98    apache_config = apache_config_tmpl.substitute(
 99        mgr_scripts=os.path.join(os.path.dirname(os.path.dirname(__file__)), 'mgr-scripts'),
100        path_prefixes=path_prefixes,
101        ## FIXME: better values:
102        tempdir=tempdir,
103        apache_pid_file=os.path.join(tempdir, 'apache.pid'),
104        mods_dir='/etc/apache2/mods-enabled',
105        includes=includes,
106        silver_instance_dir=os.path.abspath(config.args.dir),
107        silver_secret_file=silver_secret_file,
108        silver_env_vars=silver_env_vars,
109        silver_funcs=os.path.join(os.path.dirname(silversupport.__file__), 'php', 'functions.php'),
110        )
111    conf_file = os.path.join(tempdir, 'apache.conf')
112    config.logger.info('Writing config to %s' % conf_file)
113    fp = open(conf_file, 'w')
114    fp.write(apache_config)
115    fp.close()
116    exe_name = search_path(['apache2', 'apache', 'httpd'])
117    config.logger.notify('Serving on http://localhost:8080')
118    ## FIXME: -X would also be an alternative to -DFOREGROUND; not sure which is better
119    ## FIXME: this logic (and call_script()) repeats stuff in devel-server.py:
120    for url in appconfig.update_fetch:
121        if url.startswith('script:'):
122            script = url[len('script:'):]
123            print 'Running update script %s' % script
124            call_script(appconfig, script)
125        else:
126            print 'Fetching update URL %s' % url
127            url = urlparse.urljoin('http://localhost:8080', url)
128            r = urllib.urlopen(url)
129            ## FIXME: handle non-200 status
130            body = r.read()
131            if body:
132                sys.stdout.write(body)
133                if not body.endswith('\n'):
134                    sys.stdout.write('\n')
135                sys.stdout.flush()
136    run([exe_name, '-f', conf_file,
137         '-d', config.args.dir, '-DFOREGROUND'])
138
139
140def call_script(app_config, script):
141    run([sys.executable, os.path.join(os.path.dirname(os.path.dirname(__file__)),
142                                      'mgr-scripts', 'call-script.py'),
143         app_config.app_dir] + shlex.split(script))
144
145
146def _turn_sigterm_into_systemexit():
147    """
148    Attempts to turn a SIGTERM exception into a SystemExit exception.
149    """
150    try:
151        import signal
152    except ImportError:
153        return
154
155    def handle_term(signo, frame):
156        raise SystemExit
157    signal.signal(signal.SIGTERM, handle_term)
158
159
160def search_path(exe_names):
161    ## FIXME: should I allow for some general environmental variable override here?
162    paths = os.environ['PATH'].split(os.path.pathsep)
163    for name in exe_names:
164        for path in paths:
165            if os.path.exists(os.path.join(path, name)):
166                return name
167    return exe_names[0]