Git based wiki inspired by Gollum
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
8.9KB

  1. from realms import config, create_app, db, __version__, cli
  2. from realms.lib.util import random_string, in_virtualenv, green, yellow, red
  3. from subprocess import call, Popen
  4. from multiprocessing import cpu_count
  5. import click
  6. import json
  7. import sys
  8. import os
  9. import pip
  10. import time
  11. # called to discover commands in modules
  12. app = create_app()
  13. def get_user():
  14. for name in ('SUDO_USER', 'LOGNAME', 'USER', 'LNAME', 'USERNAME'):
  15. user = os.environ.get(name)
  16. if user:
  17. return user
  18. def get_pid():
  19. try:
  20. with file(config.PIDFILE) as f:
  21. return f.read().strip()
  22. except IOError:
  23. return None
  24. def is_running(pid):
  25. if not pid:
  26. return False
  27. pid = int(pid)
  28. try:
  29. os.kill(pid, 0)
  30. except OSError:
  31. return False
  32. return True
  33. def module_exists(module_name):
  34. try:
  35. __import__(module_name)
  36. except ImportError:
  37. return False
  38. else:
  39. return True
  40. @cli.command()
  41. @click.option('--site-title',
  42. default=config.SITE_TITLE,
  43. prompt='Enter site title.')
  44. @click.option('--base_url',
  45. default=config.BASE_URL,
  46. prompt='Enter base URL.')
  47. @click.option('--port',
  48. default=config.PORT,
  49. prompt='Enter port number.')
  50. @click.option('--secret-key',
  51. default=config.SECRET_KEY if config.SECRET_KEY != "CHANGE_ME" else random_string(64),
  52. prompt='Enter secret key.')
  53. @click.option('--wiki-path',
  54. default=config.WIKI_PATH,
  55. prompt='Enter wiki data directory.',
  56. help='Wiki Directory (git repo)')
  57. @click.option('--allow-anon',
  58. default=config.ALLOW_ANON,
  59. is_flag=True,
  60. prompt='Allow anonymous edits?')
  61. @click.option('--registration-enabled',
  62. default=config.REGISTRATION_ENABLED,
  63. is_flag=True,
  64. prompt='Enable registration?')
  65. @click.option('--cache-type',
  66. default=config.CACHE_TYPE,
  67. type=click.Choice([None, 'simple', 'redis', 'memcached']),
  68. prompt='Cache type?')
  69. @click.option('--db-uri',
  70. default=config.DB_URI,
  71. prompt='Database URI? Examples: http://goo.gl/RyW0cl')
  72. @click.pass_context
  73. def setup(ctx, **kw):
  74. """ Start setup wizard
  75. """
  76. try:
  77. os.mkdir('/etc/realms-wiki')
  78. except OSError:
  79. pass
  80. conf = {}
  81. for k, v in kw.items():
  82. conf[k.upper()] = v
  83. conf_path = config.update(conf)
  84. if conf['CACHE_TYPE'] == 'redis':
  85. ctx.invoke(setup_redis)
  86. elif conf['CACHE_TYPE'] == 'memcached':
  87. ctx.invoke(setup_memcached)
  88. green('Config saved to %s' % conf_path)
  89. if not conf_path.startswith('/etc/realms-wiki'):
  90. yellow('Note: You can move file to /etc/realms-wiki/realms-wiki.json')
  91. click.echo()
  92. yellow('Type "realms-wiki start" to start server')
  93. yellow('Type "realms-wiki dev" to start server in development mode')
  94. yellow('Full usage: realms-wiki --help')
  95. @click.command()
  96. @click.option('--cache-redis-host',
  97. default=getattr(config, 'CACHE_REDIS_HOST', "127.0.0.1"),
  98. prompt='Redis host')
  99. @click.option('--cache-redis-port',
  100. default=getattr(config, 'CACHE_REDIS_POST', 6379),
  101. prompt='Redis port')
  102. @click.option('--cache-redis-password',
  103. default=getattr(config, 'CACHE_REDIS_PASSWORD', None),
  104. prompt='Redis password')
  105. @click.option('--cache-redis-db',
  106. default=getattr(config, 'CACHE_REDIS_DB', 0),
  107. prompt='Redis db')
  108. def setup_redis(**kw):
  109. conf = {}
  110. for k, v in kw.items():
  111. conf[k.upper()] = v
  112. config.update(conf)
  113. install_redis()
  114. def get_prefix():
  115. return sys.prefix
  116. @cli.command(name='pip')
  117. @click.argument('cmd', nargs=-1)
  118. def pip_(cmd):
  119. """ Execute pip commands, useful for virtualenvs
  120. """
  121. pip.main(cmd)
  122. def install_redis():
  123. pip.main(['install', 'redis'])
  124. def install_mysql():
  125. pip.main(['install', 'MySQL-Python'])
  126. def install_postgres():
  127. pip.main(['install', 'psycopg2'])
  128. def install_crate():
  129. pip.main(['install', 'crate'])
  130. def install_memcached():
  131. pip.main(['install', 'python-memcached'])
  132. @click.command()
  133. @click.option('--cache-memcached-servers',
  134. default=getattr(config, 'CACHE_MEMCACHED_SERVERS', ["127.0.0.1:11211"]),
  135. type=click.STRING,
  136. prompt='Memcached servers, separate with a space')
  137. def setup_memcached(**kw):
  138. conf = {}
  139. for k, v in kw.items():
  140. conf[k.upper()] = v
  141. config.update(conf)
  142. @cli.command()
  143. @click.option('--user',
  144. default=get_user(),
  145. type=click.STRING,
  146. prompt='Run as which user? (it must exist)')
  147. @click.option('--port',
  148. default=config.PORT,
  149. type=click.INT,
  150. prompt='What port to listen on?')
  151. @click.option('--workers',
  152. default=cpu_count() * 2 + 1,
  153. type=click.INT,
  154. prompt="Number of workers? (defaults to ncpu*2+1)")
  155. def setup_upstart(**kwargs):
  156. """ Start upstart conf creation wizard
  157. """
  158. from realms.lib.util import upstart_script
  159. if in_virtualenv():
  160. app_dir = get_prefix()
  161. path = '/'.join(sys.executable.split('/')[:-1])
  162. else:
  163. # Assumed root install, not sure if this matters?
  164. app_dir = '/'
  165. path = None
  166. kwargs.update(dict(app_dir=app_dir, path=path))
  167. conf_file = '/etc/init/realms-wiki.conf'
  168. script = upstart_script(**kwargs)
  169. try:
  170. with open(conf_file, 'w') as f:
  171. f.write(script)
  172. green('Wrote file to %s' % conf_file)
  173. except IOError:
  174. with open('/tmp/realms-wiki.conf', 'w') as f:
  175. f.write(script)
  176. yellow("Wrote file to /tmp/realms-wiki.conf, to install type:")
  177. yellow("sudo mv /tmp/realms-wiki.conf /etc/init/realms-wiki.conf")
  178. click.echo()
  179. click.echo("Upstart usage:")
  180. green("sudo start realms-wiki")
  181. green("sudo stop realms-wiki")
  182. green("sudo restart realms-wiki")
  183. green("sudo status realms-wiki")
  184. @cli.command()
  185. @click.argument('json_string')
  186. def configure(json_string):
  187. """ Set config, expects JSON encoded string
  188. """
  189. try:
  190. config.update(json.loads(json_string))
  191. except ValueError, e:
  192. red('Config value should be valid JSON')
  193. @cli.command()
  194. @click.option('--port', default=config.PORT)
  195. def dev(port):
  196. """ Run development server
  197. """
  198. green("Starting development server")
  199. config_path = config.get_path()
  200. if config_path:
  201. green("Using config: %s" % config_path)
  202. else:
  203. yellow("Using default configuration")
  204. create_app().run(host="0.0.0.0",
  205. port=port,
  206. debug=True)
  207. def start_server():
  208. if is_running(get_pid()):
  209. yellow("Server is already running")
  210. return
  211. flags = '--daemon --pid %s' % config.PIDFILE
  212. green("Server started. Port: %s" % config.PORT)
  213. config_path = config.get_path()
  214. if config_path:
  215. green("Using config: %s" % config_path)
  216. else:
  217. yellow("Using default configuration")
  218. Popen("gunicorn 'realms:create_app()' -b 0.0.0.0:%s -k gevent %s" %
  219. (config.PORT, flags), shell=True, executable='/bin/bash')
  220. def stop_server():
  221. pid = get_pid()
  222. if not is_running(pid):
  223. yellow("Server is not running")
  224. else:
  225. yellow("Shutting down server")
  226. call(['kill', pid])
  227. while is_running(pid):
  228. time.sleep(1)
  229. @cli.command()
  230. def run():
  231. """ Run production server (alias for start)
  232. """
  233. start_server()
  234. @cli.command()
  235. def start():
  236. """ Run server daemon
  237. """
  238. start_server()
  239. @cli.command()
  240. def stop():
  241. """ Stop server
  242. """
  243. stop_server()
  244. @cli.command()
  245. def restart():
  246. """ Restart server
  247. """
  248. stop_server()
  249. start_server()
  250. @cli.command()
  251. def status():
  252. """ Get server status
  253. """
  254. pid = get_pid()
  255. if not is_running(pid):
  256. yellow("Server is not running")
  257. else:
  258. green("Server is running PID: %s" % pid)
  259. @cli.command()
  260. def create_db():
  261. """ Creates DB tables
  262. """
  263. green("Creating all tables")
  264. with app.app_context():
  265. green('DB_URI: %s' % app.config.get('DB_URI'))
  266. db.metadata.create_all(db.get_engine(app))
  267. @cli.command()
  268. @click.confirmation_option(help='Are you sure you want to drop the db?')
  269. def drop_db():
  270. """ Drops DB tables
  271. """
  272. yellow("Dropping all tables")
  273. with app.app_context():
  274. db.metadata.drop_all(db.get_engine(app))
  275. @cli.command()
  276. def test():
  277. """ Run tests
  278. """
  279. for mod in [('flask.ext.testing', 'Flask-Testing'), ('nose', 'nose'), ('blinker', 'blinker')]:
  280. if not module_exists(mod[0]):
  281. pip.main(['install', mod[1]])
  282. nosetests = get_prefix() + "/bin/nosetests" if in_virtualenv() else "nosetests"
  283. call([nosetests, 'realms'])
  284. @cli.command()
  285. def version():
  286. """ Output version
  287. """
  288. green(__version__)
  289. if __name__ == '__main__':
  290. cli()