realms-wiki/realms/cli.py

358 рядки
8.3 KiB
Python

from realms import config, app, cli, db
from realms.lib.util import random_string
from subprocess import call, Popen
from multiprocessing import cpu_count
import click
import json
import sys
import os
def check_su(f):
if not in_virtualenv() and not is_su():
# This does not account for people the have user level python installs
# that aren't virtual environments! Should be rare I think
red("This command requires root privileges, use sudo or run as root.")
sys.exit()
return f
def get_user():
for name in ('SUDO_USER', 'LOGNAME', 'USER', 'LNAME', 'USERNAME'):
user = os.environ.get(name)
if user:
return user
def in_virtualenv():
return hasattr(sys, 'real_prefix')
def is_su():
return os.geteuid() == 0
def get_pid():
try:
with file(config.PIDFILE) as f:
pid = f.read().strip()
return pid if pid and int(pid) > 0 and not call(['kill', '-s', '0', pid]) else False
except IOError:
return False
def module_exists(module_name):
try:
__import__(module_name)
except ImportError:
return False
else:
return True
def green(s):
click.secho(s, fg='green')
def yellow(s):
click.secho(s, fg='yellow')
def red(s):
click.secho(s, fg='red')
@cli.command()
@check_su
@click.option('--site-title',
default=config.SITE_TITLE,
prompt='Enter site title.')
@click.option('--base_url',
default=config.BASE_URL,
prompt='Enter base URL.')
@click.option('--port',
default=config.PORT,
prompt='Enter port number.')
@click.option('--secret-key',
default=config.SECRET_KEY if config.SECRET_KEY != "CHANGE_ME" else random_string(64),
prompt='Enter secret key.')
@click.option('--wiki-path',
default=config.WIKI_PATH,
prompt='Enter wiki data directory.',
help='Wiki Directory (git repo)')
@click.option('--allow-anon',
default=config.ALLOW_ANON,
is_flag=True,
prompt='Allow anonymous edits?')
@click.option('--registration-enabled',
default=config.REGISTRATION_ENABLED,
is_flag=True,
prompt='Enable registration?')
@click.option('--cache-type',
default=config.CACHE_TYPE,
type=click.Choice([None, 'simple', 'redis', 'memcached']),
prompt='Cache type?')
@click.option('--db-uri',
default=config.DB_URI,
prompt='Database URI? Examples: http://goo.gl/RyW0cl')
@click.pass_context
def setup(ctx, **kw):
""" Start setup wizard
"""
conf = {}
for k, v in kw.items():
conf[k.upper()] = v
conf_path = config.update(conf)
if conf['CACHE_TYPE'] == 'redis':
ctx.invoke(setup_redis)
elif conf['CACHE_TYPE'] == 'memcached':
ctx.invoke(setup_memcached)
green('Config saved to %s' % conf_path)
yellow('Type "realms-wiki start" to start server')
yellow('Type "realms-wiki dev" to start server in development mode')
@click.command()
@click.option('--cache-redis-host',
default=getattr(config, 'CACHE_REDIS_HOST', "127.0.0.1"),
prompt='Redis host')
@click.option('--cache-redis-port',
default=getattr(config, 'CACHE_REDIS_POST', 6379),
prompt='Redis port')
@click.option('--cache-redis-password',
default=getattr(config, 'CACHE_REDIS_PASSWORD', None),
prompt='Redis password')
@click.option('--cache-redis-db',
default=getattr(config, 'CACHE_REDIS_DB', 0),
prompt='Redis db')
def setup_redis(**kw):
conf = {}
for k, v in kw.items():
conf[k.upper()] = v
config.update(conf)
install_redis()
def get_prefix():
return sys.prefix
def get_pip():
""" Get virtualenv path for pip
"""
if in_virtualenv():
return get_prefix() + '/bin/pip'
else:
return 'pip'
@cli.command()
@check_su
@click.argument('cmd', nargs=-1)
def pip(cmd):
""" Execute pip commands, useful for virtualenvs
"""
call(get_pip() + ' ' + ' '.join(cmd), shell=True)
def install_redis():
call([get_pip(), 'install', 'redis'])
def install_mysql():
call([get_pip(), 'install', 'MySQL-Python'])
def install_postgres():
call([get_pip(), 'install', 'psycopg2'])
def install_memcached():
call([get_pip(), 'install', 'python-memcached'])
@click.command()
@click.option('--cache-memcached-servers',
default=getattr(config, 'CACHE_MEMCACHED_SERVERS', ["127.0.0.1:11211"]),
type=click.STRING,
prompt='Memcached servers, separate with a space')
def setup_memcached(**kw):
conf = {}
for k, v in kw.items():
conf[k.upper()] = v
config.update(conf)
@cli.command()
@check_su
@click.option('--user',
default=get_user(),
type=click.STRING,
prompt='Run as which user? (it must exist)')
@click.option('--port',
default=config.PORT,
type=click.INT,
prompt='What port to listen on?')
@click.option('--workers',
default=cpu_count() * 2 + 1,
type=click.INT,
prompt="Number of workers? (defaults to ncpu*2+1)")
def setup_upstart(**kwargs):
""" Start upstart conf creation wizard
"""
from realms.lib.util import upstart_script
if in_virtualenv():
app_dir = get_prefix()
path = '/'.join(sys.executable.split('/')[:-1])
else:
# Assumed root install, not sure if this matters?
app_dir = '/'
path = None
kwargs.update(dict(app_dir=app_dir, path=path))
conf_file = '/etc/init/realms-wiki.conf'
with open('/etc/init/realms-wiki.conf', 'w') as f:
f.write(upstart_script(**kwargs))
green('Wrote file to %s' % conf_file)
green("Type 'sudo start realms-wiki' to start")
green("Type 'sudo stop realms-wiki' to stop")
green("Type 'sudo restart realms-wiki' to restart")
@cli.command()
@click.argument('json_string')
def configure(json_string):
""" Set config.json, expects JSON encoded string
"""
try:
config.update(json.loads(json_string))
except ValueError, e:
red('Config value should be valid JSON')
@cli.command()
@click.option('--port', default=5000)
def dev(port):
""" Run development server
"""
green("Starting development server")
app.run(host="0.0.0.0",
port=port,
debug=True)
def start_server():
if get_pid():
yellow("Server is already running")
return
flags = '--daemon --pid %s' % config.PIDFILE
green("Server started. Port: %s" % config.PORT)
Popen('gunicorn realms:app -b 0.0.0.0:%s -k gevent %s' %
(config.PORT, flags), shell=True, executable='/bin/bash')
def stop_server():
pid = get_pid()
if not pid:
yellow("Server is not running")
else:
yellow("Shutting down server")
call(['kill', pid])
@cli.command()
def run():
""" Run production server (alias for start)
"""
start_server()
@cli.command()
def start():
""" Run server daemon
"""
start_server()
@cli.command()
def stop():
""" Stop server
"""
stop_server()
@cli.command()
def restart():
""" Restart server
"""
stop_server()
start_server()
@cli.command()
def status():
""" Get server status
"""
pid = get_pid()
if not pid:
yellow("Server is not running")
else:
green("Server is running PID: %s" % pid)
@cli.command()
def create_db():
""" Creates DB tables
"""
green("Creating all tables")
db.create_all()
@cli.command()
@click.confirmation_option(help='Are you sure you want to drop the db?')
def drop_db():
""" Drops DB tables
"""
yellow("Dropping all tables")
db.drop_all()
@cli.command()
def test():
""" Run tests
"""
for mod in [('flask.ext.testing', 'Flask-Testing'), ('nose', 'nose')]:
if not module_exists(mod[0]):
call([get_pip(), 'install', mod[1]])
nosetests = get_prefix() + "/bin/nosetests" if in_virtualenv() else "nosetests"
call([nosetests, config.APP_PATH])
@cli.command()
def version():
""" Output version
"""
with open('VERSION') as f:
return f.read().strip()
if __name__ == '__main__':
cli()