majuna/app/__init__.py

172 lines
7.3 KiB
Python
Raw Normal View History

import os
2024-11-10 13:38:51 +00:00
import sys
from typing import Iterator
2024-11-10 13:38:51 +00:00
import yaml
from flask import Flask, redirect, url_for, send_from_directory
2022-05-16 11:44:03 +01:00
from flask.typing import ResponseReturnValue
from prometheus_client import make_wsgi_app, Metric, CollectorRegistry
from prometheus_client.metrics_core import GaugeMetricFamily, CounterMetricFamily
from prometheus_client.registry import Collector, REGISTRY
from sqlalchemy import text
from sqlalchemy.exc import SQLAlchemyError
2022-10-05 15:25:51 +01:00
from werkzeug.middleware.dispatcher import DispatcherMiddleware
2022-03-10 14:26:22 +00:00
2024-11-10 13:38:51 +00:00
from app.api import api
from app.extensions import bootstrap
2022-03-10 14:26:22 +00:00
from app.extensions import db
from app.extensions import migrate
from app.models.automation import Automation, AutomationState
2022-03-10 14:26:22 +00:00
from app.portal import portal
2023-10-23 17:33:17 +01:00
from app.portal.report import report
from app.tfstate import tfstate
2024-12-04 13:02:01 +00:00
from prometheus_flask_exporter import PrometheusMetrics
2022-03-10 14:26:22 +00:00
app = Flask(__name__)
app.config.from_file("../config.yaml", load=yaml.safe_load)
2022-10-05 15:25:51 +01:00
# create new registry to avoid multiple metrics registration in global REGISTRY
registry = CollectorRegistry()
2024-12-04 13:02:01 +00:00
metrics = PrometheusMetrics(app, registry=registry)
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, { # type: ignore[method-assign]
'/metrics': make_wsgi_app(registry)
2022-10-05 15:25:51 +01:00
})
2024-12-04 13:02:01 +00:00
# register default collectors to our new registry
collectors = list(REGISTRY._collector_to_names.keys())
for collector in collectors:
registry.register(collector)
2024-11-10 13:38:51 +00:00
db.init_app(app)
2022-03-10 14:26:22 +00:00
migrate.init_app(app, db, render_as_batch=True)
bootstrap.init_app(app)
2024-11-10 13:38:51 +00:00
app.register_blueprint(api, url_prefix="/api")
2022-03-10 14:26:22 +00:00
app.register_blueprint(portal, url_prefix="/portal")
app.register_blueprint(tfstate, url_prefix="/tfstate")
2023-10-23 17:33:17 +01:00
app.register_blueprint(report, url_prefix="/report")
2022-10-05 15:25:51 +01:00
2023-10-23 17:53:48 +01:00
def not_migrating() -> bool:
return len(sys.argv) < 2 or sys.argv[1] != "db"
class DefinedProxiesCollector(Collector):
def collect(self) -> Iterator[Metric]:
with app.app_context():
ok = GaugeMetricFamily("database_collector",
"Status of a database collector (0: bad, 1: good)",
labels=["collector"])
try:
with db.engine.connect() as conn:
result = conn.execute(text("""
SELECT origin.group_id, "group".group_name, proxy.provider, proxy.pool_id, pool.pool_name,
COUNT(proxy.id) FROM proxy, origin, pool, "group"
WHERE proxy.origin_id = origin.id
AND origin.group_id = "group".id
AND proxy.pool_id = pool.id
AND proxy.destroyed IS NULL
GROUP BY origin.group_id, "group".group_name, proxy.provider, proxy.pool_id, pool.pool_name;
"""))
c = GaugeMetricFamily("defined_proxies", "Number of proxies currently defined for deployment",
labels=['group_id', 'group_name', 'provider', 'pool_id',
'pool_name'])
for row in result:
c.add_metric([str(row[0]), row[1], row[2], str(row[3]), row[4]],
row[5])
yield c
ok.add_metric(["defined_proxies"], 1)
except SQLAlchemyError:
ok.add_metric(["defined_proxies"], 0)
yield ok
2024-12-03 16:13:00 +00:00
class BlockedProxiesCollector(Collector):
def collect(self) -> Iterator[Metric]:
with app.app_context():
ok = GaugeMetricFamily("database_collector",
"Status of a database collector (0: bad, 1: good)",
labels=["collector"])
try:
with db.engine.connect() as conn:
result = conn.execute(text("""
SELECT origin.group_id, "group".group_name, proxy.provider, proxy.pool_id, pool.pool_name,
proxy.deprecation_reason, COUNT(proxy.id) FROM proxy, origin, pool, "group"
WHERE proxy.origin_id = origin.id
AND origin.group_id = "group".id
AND proxy.pool_id = pool.id
AND proxy.deprecated IS NOT NULL
GROUP BY origin.group_id, "group".group_name, proxy.provider, proxy.pool_id, pool.pool_name,
proxy.deprecation_reason;
"""))
c = CounterMetricFamily("deprecated_proxies",
"Number of proxies deprecated",
labels=['group_id', 'group_name', 'provider', 'pool_id', 'pool_name',
'deprecation_reason'])
for row in result:
c.add_metric([str(row[0]), row[1], row[2], str(row[3]), row[4], row[5]],
row[6])
yield c
ok.add_metric(["deprecated_proxies"], 0)
except SQLAlchemyError:
ok.add_metric(["deprecated_proxies"], 0)
yield ok
class AutomationCollector(Collector):
def collect(self) -> Iterator[Metric]:
with app.app_context():
ok = GaugeMetricFamily("database_collector",
"Status of a database collector (0: bad, 1: good)",
labels=["collector"])
try:
c = GaugeMetricFamily("automation_state", "The automation state (0: idle, 1: running, 2: error)",
labels=['automation_name'])
automations = Automation.query.all()
for automation in automations:
if automation.short_name in app.config['HIDDEN_AUTOMATIONS']:
continue
if automation.state == AutomationState.IDLE:
c.add_metric([automation.short_name], 0)
elif automation.state == AutomationState.RUNNING:
c.add_metric([automation.short_name], 1)
else:
c.add_metric([automation.short_name], 2)
yield c
ok.add_metric(["automation_state"], 1)
except SQLAlchemyError:
ok.add_metric(["automation_state"], 0)
yield ok
2024-12-04 13:02:01 +00:00
# register all custom collectors to registry
if not_migrating() and 'DISABLE_METRICS' not in os.environ:
registry.register(DefinedProxiesCollector())
registry.register(BlockedProxiesCollector())
registry.register(AutomationCollector())
2022-10-05 15:25:51 +01:00
@app.route('/ui')
2024-11-10 13:38:51 +00:00
def redirect_ui() -> ResponseReturnValue:
return redirect("/ui/")
2024-11-10 13:38:51 +00:00
@app.route('/ui/', defaults={'path': ''})
@app.route('/ui/<path:path>')
2024-11-10 13:38:51 +00:00
def serve_ui(path: str) -> ResponseReturnValue:
if path != "" and os.path.exists("app/static/ui/" + path):
return send_from_directory('static/ui', path)
else:
return send_from_directory('static/ui', 'index.html')
2024-11-10 13:38:51 +00:00
2022-03-10 14:26:22 +00:00
@app.route('/')
2022-05-16 11:44:03 +01:00
def index() -> ResponseReturnValue:
# TODO: update to point at new UI when ready
2022-03-10 14:26:22 +00:00
return redirect(url_for("portal.portal_home"))
if __name__ == '__main__':
app.run()