server.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.cli.plugins
  4. ~~~~~~~~~~~~~~~~~~~
  5. This module contains all plugin commands.
  6. :copyright: (c) 2016 by the FlaskBB Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import os
  10. import signal
  11. from flask.cli import pass_script_info
  12. import click
  13. from flaskbb._compat import iteritems
  14. from flaskbb.cli.main import flaskbb
  15. from flaskbb.cli.utils import FlaskBBCLIError
  16. def get_gunicorn_pid(pid, instance_path):
  17. if os.path.exists(pid): # pidfile provided
  18. with open(pid) as pidfile:
  19. pid = int(pidfile.readline().strip())
  20. elif pid: # pid provided
  21. pid = int(pid)
  22. try:
  23. os.kill(pid, 0)
  24. except OSError:
  25. pid = None
  26. else: # nothing provided, lets try to get the pid from flaskbb.pid
  27. pid = None
  28. pidfile = os.path.join(instance_path, "flaskbb.pid")
  29. if os.path.exists(pidfile):
  30. with open(pidfile) as f:
  31. pid = int(f.readline().strip())
  32. return pid
  33. @flaskbb.group()
  34. def server():
  35. """Manages the start and stop process of the gunicorn WSGI server. \n
  36. Gunicorn is made for UNIX-like operating systems and thus Windows is not
  37. supported.\n
  38. For proper monitoring of the Gunicorn/FlaskBB process it is advised
  39. to use a real process monitoring system like 'supervisord' or
  40. 'systemd'.
  41. """
  42. pass
  43. @server.command()
  44. @click.option("--host", "-h", default="127.0.0.1", show_default=True,
  45. help="The interface to bind to.")
  46. @click.option("--port", "-p", default="8000", type=int, show_default=True,
  47. help="The port to bind to.")
  48. @click.option("--workers", "-w", default=4, show_default=True,
  49. help="The number of worker processes for handling requests.")
  50. @click.option("--daemon", "-d", default=False, is_flag=True, show_default=True,
  51. help="Starts gunicorn as daemon.")
  52. @click.option("--pid", "-p", default=None, help="Path to a PID file. "
  53. "If the instance directory exists, it will store the Pidfile "
  54. "there.")
  55. @pass_script_info
  56. def start(info, host, port, workers, daemon, pid):
  57. """Starts a preconfigured gunicorn instance."""
  58. try:
  59. from gunicorn.app.base import Application
  60. except ImportError:
  61. raise FlaskBBCLIError("Cannot import gunicorn. "
  62. "Make sure it is installed.", fg="red")
  63. class FlaskBBApplication(Application):
  64. def __init__(self, app, options=None, args=None):
  65. self.options = options or {}
  66. self.application = app
  67. super(FlaskBBApplication, self).__init__()
  68. def load_config(self):
  69. config = dict([
  70. (key, value) for key, value in iteritems(self.options)
  71. if key in self.cfg.settings and value is not None
  72. ])
  73. for key, value in iteritems(config):
  74. print(key, value)
  75. self.cfg.set(key.lower(), value)
  76. def load(self):
  77. return self.application
  78. flaskbb_app = info.load_app()
  79. default_pid = None
  80. if os.path.exists(flaskbb_app.instance_path):
  81. default_pid = os.path.join(flaskbb_app.instance_path, "flaskbb.pid")
  82. options = {
  83. "bind": "{}:{}".format(host, port),
  84. "workers": workers,
  85. "daemon": daemon,
  86. "pidfile": pid or default_pid
  87. }
  88. FlaskBBApplication(flaskbb_app, options).run()
  89. @server.command()
  90. @click.option("--pid", "-p", default="", help="The PID or the path to "
  91. "the PID file. By default it tries to get the PID file from the "
  92. "instance folder.")
  93. @click.option("--force", "-f", default=False, is_flag=True, show_default=True,
  94. help="Kills gunicorn ungratefully.")
  95. @pass_script_info
  96. def stop(info, pid, force):
  97. """Stops the gunicorn process."""
  98. app = info.load_app()
  99. pid = get_gunicorn_pid(pid, app.instance_path)
  100. if pid is None:
  101. raise FlaskBBCLIError("Neither a valid PID File nor a PID that exist "
  102. "was provided.", fg="red")
  103. try:
  104. if force:
  105. os.kill(pid, signal.SIGKILL)
  106. else:
  107. os.kill(pid, signal.SIGTERM)
  108. except ProcessLookupError:
  109. click.secho("Process with PID '{}' not found. Are you sure that "
  110. "Gunicorn/FlaskBB is running?".format(pid), fg="yellow")
  111. @server.command()
  112. @click.option("--pid", "-p", default="", help="The PID or the path to "
  113. "the PID file. By default it tries to get the PID file from the "
  114. "instance folder.")
  115. @pass_script_info
  116. def status(info, pid):
  117. """Shows the status of gunicorn."""
  118. app = info.load_app()
  119. pid = get_gunicorn_pid(pid, app.instance_path)
  120. if pid is None:
  121. click.secho("Gunicorn/FlaskBB is not running.", fg="red")
  122. else:
  123. click.secho("Gunicorn/FlaskBB is running.", fg="green")