1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00

verify redis is running prior to starting app

This commit is contained in:
checktheroads
2019-12-30 09:43:49 -07:00
parent b8dc7826fe
commit 590ab2aa59
3 changed files with 111 additions and 63 deletions

View File

@@ -1,13 +1,15 @@
"""Hyperglass Front End.""" """Hyperglass Front End."""
# Standard Library Imports # Standard Library Imports
import asyncio
import operator import operator
import os
import tempfile
import time import time
from pathlib import Path from pathlib import Path
# Third Party Imports # Third Party Imports
import aredis import aredis
import stackprinter
from prometheus_client import CONTENT_TYPE_LATEST from prometheus_client import CONTENT_TYPE_LATEST
from prometheus_client import CollectorRegistry from prometheus_client import CollectorRegistry
from prometheus_client import Counter from prometheus_client import Counter
@@ -37,11 +39,32 @@ from hyperglass.exceptions import ResponseEmpty
from hyperglass.exceptions import RestError from hyperglass.exceptions import RestError
from hyperglass.exceptions import ScrapeError from hyperglass.exceptions import ScrapeError
from hyperglass.render import render_html from hyperglass.render import render_html
from hyperglass.util import cpu_count
from hyperglass.util import log from hyperglass.util import log
stackprinter.set_excepthook() log.debug(f"Configuration Parameters: {params.dict(by_alias=True)}")
log.debug(f"Configuration Parameters:\n {params.dict()}") tempdir = tempfile.TemporaryDirectory(prefix="hyperglass_")
os.environ["prometheus_multiproc_dir"] = tempdir.name
# Static File Definitions
static_dir = Path(__file__).parent / "static" / "ui"
log.debug(f"Static Files: {static_dir}")
# Main Sanic App Definition
app = Sanic(__name__)
app.static("/ui", str(static_dir))
log.debug(app.config)
# Sanic Web Server Parameters
APP_PARAMS = {
"host": params.general.listen_address,
"port": params.general.listen_port,
"debug": params.general.debug,
"workers": cpu_count(),
"access_log": params.general.debug,
"auto_reload": params.general.debug,
}
# Redis Config # Redis Config
redis_config = { redis_config = {
@@ -50,28 +73,14 @@ redis_config = {
"decode_responses": True, "decode_responses": True,
} }
# Static File Definitions
static_dir = Path(__file__).parent / "static" / "ui"
# Main Sanic app definition
log.debug(f"Static Files: {static_dir}")
app = Sanic(__name__)
app.static("/ui", str(static_dir))
log.debug(app.config)
# Redis Cache Config
r_cache = aredis.StrictRedis(db=params.features.cache.redis_id, **redis_config)
# Sanic-Limiter Config # Sanic-Limiter Config
query_rate = params.features.rate_limit.query.rate query_rate = params.features.rate_limit.query.rate
query_period = params.features.rate_limit.query.period query_period = params.features.rate_limit.query.period
site_rate = params.features.rate_limit.site.rate site_rate = params.features.rate_limit.site.rate
site_period = params.features.rate_limit.site.period site_period = params.features.rate_limit.site.period
#
rate_limit_query = f"{query_rate} per {query_period}" rate_limit_query = f"{query_rate} per {query_period}"
rate_limit_site = f"{site_rate} per {site_period}" rate_limit_site = f"{site_rate} per {site_period}"
log.debug(f"Query rate limit: {rate_limit_query}") log.debug(f"Query rate limit: {rate_limit_query}")
log.debug(f"Site rate limit: {rate_limit_site}") log.debug(f"Site rate limit: {rate_limit_site}")
@@ -82,8 +91,33 @@ r_limiter_url = "redis://{host}:{port}/{db}".format(
port=params.general.redis_port, port=params.general.redis_port,
db=params.features.rate_limit.redis_id, db=params.features.rate_limit.redis_id,
) )
r_cache = aredis.StrictRedis(db=params.features.cache.redis_id, **redis_config)
r_limiter = aredis.StrictRedis(db=params.features.rate_limit.redis_id, **redis_config) r_limiter = aredis.StrictRedis(db=params.features.rate_limit.redis_id, **redis_config)
async def check_redis():
"""Ensure Redis is running before starting server.
Raises:
HyperglassError: Raised if Redis is not running.
Returns:
{bool} -- True if Redis is running.
"""
try:
await r_cache.echo("hyperglass test")
await r_limiter.echo("hyperglass test")
except Exception:
raise HyperglassError(
f"Redis isn't running at: {redis_config['host']}:{redis_config['port']}",
alert="danger",
) from None
return True
# Verify Redis is running
asyncio.run(check_redis())
# Adds Sanic config variable for Sanic-Limiter # Adds Sanic config variable for Sanic-Limiter
app.config.update(RATELIMIT_STORAGE_URL=r_limiter_url) app.config.update(RATELIMIT_STORAGE_URL=r_limiter_url)
@@ -128,7 +162,7 @@ async def metrics(request):
@app.exception(InvalidUsage) @app.exception(InvalidUsage)
async def handle_frontend_errors(request, exception): async def handle_frontend_errors(request, exception):
"""Handles user-facing feedback related to frontend/input errors""" """Handles user-facing feedback related to frontend/input errors."""
client_addr = get_remote_address(request) client_addr = get_remote_address(request)
error = exception.args[0] error = exception.args[0]
alert = error["alert"] alert = error["alert"]
@@ -149,7 +183,7 @@ async def handle_frontend_errors(request, exception):
@app.exception(ServiceUnavailable) @app.exception(ServiceUnavailable)
async def handle_backend_errors(request, exception): async def handle_backend_errors(request, exception):
"""Handles user-facing feedback related to backend errors""" """Handles user-facing feedback related to backend errors."""
client_addr = get_remote_address(request) client_addr = get_remote_address(request)
error = exception.args[0] error = exception.args[0]
alert = error["alert"] alert = error["alert"]
@@ -170,7 +204,7 @@ async def handle_backend_errors(request, exception):
@app.exception(NotFound) @app.exception(NotFound)
async def handle_404(request, exception): async def handle_404(request, exception):
"""Renders full error page for invalid URI""" """Renders full error page for invalid URI."""
path = request.path path = request.path
html = render_html("404", uri=path) html = render_html("404", uri=path)
client_addr = get_remote_address(request) client_addr = get_remote_address(request)
@@ -181,7 +215,7 @@ async def handle_404(request, exception):
@app.exception(RateLimitExceeded) @app.exception(RateLimitExceeded)
async def handle_429(request, exception): async def handle_429(request, exception):
"""Renders full error page for too many site queries""" """Renders full error page for too many site queries."""
html = render_html("ratelimit-site") html = render_html("ratelimit-site")
client_addr = get_remote_address(request) client_addr = get_remote_address(request)
count_ratelimit.labels(exception, client_addr).inc() count_ratelimit.labels(exception, client_addr).inc()
@@ -191,7 +225,7 @@ async def handle_429(request, exception):
@app.exception(ServerError) @app.exception(ServerError)
async def handle_500(request, exception): async def handle_500(request, exception):
"""General Error Page""" """General Error Page."""
client_addr = get_remote_address(request) client_addr = get_remote_address(request)
count_errors.labels(500, exception, client_addr, None, None, None).inc() count_errors.labels(500, exception, client_addr, None, None, None).inc()
log.error(f"Error: {exception}, Source: {client_addr}") log.error(f"Error: {exception}, Source: {client_addr}")
@@ -200,7 +234,7 @@ async def handle_500(request, exception):
async def clear_cache(): async def clear_cache():
"""Function to clear the Redis cache""" """Function to clear the Redis cache."""
try: try:
await r_cache.flushdb() await r_cache.flushdb()
return "Successfully cleared cache" return "Successfully cleared cache"
@@ -212,20 +246,20 @@ async def clear_cache():
@app.route("/", methods=["GET"]) @app.route("/", methods=["GET"])
@limiter.limit(rate_limit_site, error_message="Site") @limiter.limit(rate_limit_site, error_message="Site")
async def site(request): async def site(request):
"""Main front-end web application""" """Main front-end web application."""
return response.html(render_html("form", primary_asn=params.general.primary_asn)) return response.html(render_html("form", primary_asn=params.general.primary_asn))
@app.route("/test", methods=["GET"]) @app.route("/test", methods=["GET"])
async def test_route(request): async def test_route(request):
"""Test route for various tests""" """Test route for various tests."""
html = render_html("500") html = render_html("500")
return response.html(html, status=500) return response.html(html, status=500)
async def validate_input(query_data): # noqa: C901 async def validate_input(query_data): # noqa: C901
""" """Delete any globally unsupported query parameters.
Deletes any globally unsupported query parameters.
Performs validation functions per input type: Performs validation functions per input type:
- query_target: - query_target:
- Verifies input is not empty - Verifies input is not empty
@@ -406,8 +440,9 @@ async def validate_input(query_data): # noqa: C901
}, },
) )
async def hyperglass_main(request): async def hyperglass_main(request):
""" """Main backend application initiator.
Main backend application initiator. Ingests Ajax POST data from
Ingests Ajax POST data from
form submit, passes it to the backend application to perform the form submit, passes it to the backend application to perform the
filtering/lookups. filtering/lookups.
""" """

View File

@@ -2,30 +2,13 @@
Hyperglass web app initiator. Launches Sanic with appropriate number of Hyperglass web app initiator. Launches Sanic with appropriate number of
workers per their documentation (equal to number of CPU cores). workers per their documentation (equal to number of CPU cores).
""" """
# Override web server listen host & port if necessary:
host = "localhost"
port = 8001
try: try:
import multiprocessing
import os import os
import tempfile import tempfile
from hyperglass import hyperglass from hyperglass import hyperglass, APP_PARAMS
from hyperglass.configuration import params
from hyperglass.configuration import stack # NOQA: F401
except ImportError as import_error: except ImportError as import_error:
raise RuntimeError(import_error) raise RuntimeError(import_error)
debug = False
access_log = True
if params.general.debug:
debug = True
access_log = False
workers = multiprocessing.cpu_count()
def start(): def start():
""" """
@@ -36,13 +19,7 @@ def start():
os.environ["prometheus_multiproc_dir"] = tempdir.name os.environ["prometheus_multiproc_dir"] = tempdir.name
try: try:
hyperglass.app.run( hyperglass.app.run(**APP_PARAMS)
host=host,
port=port,
debug=params.general.debug,
workers=workers,
access_log=access_log,
)
except Exception as hyperglass_error: except Exception as hyperglass_error:
raise RuntimeError(hyperglass_error) raise RuntimeError(hyperglass_error)

View File

@@ -30,6 +30,17 @@ cp = shutil.copyfile
# Define working directory # Define working directory
working_directory = os.path.dirname(os.path.abspath(__file__)) working_directory = os.path.dirname(os.path.abspath(__file__))
# Helpers
NL = "\n"
WS1 = " "
WS2 = " "
WS4 = " "
WS6 = " "
WS8 = " "
CL = ":"
E_ROCKET = "\U0001F680"
E_SPARKLES = "\U00002728"
def async_command(func): def async_command(func):
func = asyncio.coroutine(func) func = asyncio.coroutine(func)
@@ -603,7 +614,7 @@ def render_hyperglass_assets():
def start_dev_server(host, port): def start_dev_server(host, port):
"""Starts Sanic development server for testing without WSGI/Reverse Proxy""" """Starts Sanic development server for testing without WSGI/Reverse Proxy"""
try: try:
from hyperglass import hyperglass from hyperglass.hyperglass import app, APP_PARAMS
from hyperglass.configuration import params from hyperglass.configuration import params
except ImportError as import_error: except ImportError as import_error:
raise click.ClickException( raise click.ClickException(
@@ -611,12 +622,37 @@ def start_dev_server(host, port):
+ click.style(import_error, fg="blue") + click.style(import_error, fg="blue")
) )
try: try:
click.secho( if host is not None:
f"✓ Starting hyperglass development server...", fg="green", bold=True APP_PARAMS["host"] = host
) if port is not None:
hyperglass.app.run( APP_PARAMS["port"] = port
host=host, debug=params.general.debug, port=port, auto_reload=False
click.echo(
click.style(
NL + f"✓ Starting hyperglass web server on...", fg="green", bold=True
)
+ NL
+ E_SPARKLES
+ NL
+ E_SPARKLES * 2
+ NL
+ E_SPARKLES * 3
+ NL
+ WS8
+ click.style("http://", fg="white")
+ click.style(str(APP_PARAMS["host"]), fg="blue", bold=True)
+ click.style(CL, fg="white")
+ click.style(str(APP_PARAMS["port"]), fg="magenta", bold=True)
+ NL
+ WS4
+ E_ROCKET
+ NL
+ NL
+ WS1
+ E_ROCKET
+ NL
) )
app.run(**APP_PARAMS)
except Exception as e: except Exception as e:
raise click.ClickException( raise click.ClickException(
click.style("✗ Failed to start test server: ", fg="red", bold=True) click.style("✗ Failed to start test server: ", fg="red", bold=True)
@@ -625,8 +661,8 @@ def start_dev_server(host, port):
@hg.command("dev-server", help="Start development web server") @hg.command("dev-server", help="Start development web server")
@click.option("--host", type=str, default="0.0.0.0", help="Listening IP") @click.option("--host", type=str, required=False, help="Listening IP")
@click.option("--port", type=int, default=5000, help="TCP Port") @click.option("--port", type=int, required=False, help="TCP Port")
@click.option( @click.option(
"--assets/--no-assets", default=False, help="Render Theme & Build Web Assets" "--assets/--no-assets", default=False, help="Render Theme & Build Web Assets"
) )