Skip to content

Commit

Permalink
Dashboard can be served on a non-root path (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
kazet authored Jan 17, 2024
1 parent c194b8a commit 8b2a11c
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 55 deletions.
67 changes: 40 additions & 27 deletions karton/dashboard/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from typing import Any, Dict, List, Optional, Tuple

import mistune # type: ignore
from flask import ( # type: ignore
from flask import (
Blueprint,
Flask,
jsonify,
redirect,
Expand All @@ -29,11 +30,6 @@

logging.basicConfig(level=logging.INFO)

app_path = Path(__file__).parent
static_folder = app_path / "static"
graph_folder = app_path / "graph"
app = Flask(__name__, static_folder=None, template_folder=str(app_path / "templates"))


class KartonDashboard(KartonBase):
identity = "karton.dashboard"
Expand All @@ -43,6 +39,17 @@ class KartonDashboard(KartonBase):

karton = KartonDashboard()

base_path = karton.config.get("dashboard", "base_path", fallback="")

app_path = Path(__file__).parent
static_folder = app_path / "static"
graph_folder = app_path / "graph"
app = Flask(__name__, static_folder=None)
blueprint = Blueprint(
"dashboard", __name__, template_folder=str(app_path / "templates")
)


markdown = mistune.create_markdown(
escape=True,
renderer="html",
Expand Down Expand Up @@ -186,12 +193,15 @@ def get_xrefs(root_uid) -> List[Tuple[str, str]]:


def add_metrics(state: KartonState, metric: KartonMetrics, key: str) -> None:
metrics = {k: int(v) for k, v in state.backend.redis.hgetall(metric.value).items()}
metrics = {
k: int(v)
for k, v in state.backend.redis.hgetall(metric.value).items() # type: ignore
}
for name, value in metrics.items():
karton_metrics.labels(key, name).set(value)


@app.route("/varz", methods=["GET"])
@blueprint.route("/varz", methods=["GET"])
def varz():
"""Update and get prometheus metrics"""

Expand All @@ -208,7 +218,7 @@ def varz():
task_infos[(safe_name, task.priority, task.status)] += 1

# set the default of active queues to 0 to avoid gaps in graphs
for (priority, status) in product(TaskPriority, TaskState):
for priority, status in product(TaskPriority, TaskState):
karton_tasks.labels(safe_name, priority.value, status.value).set(0)

for (name, priority, status), count in task_infos.items():
Expand All @@ -226,18 +236,18 @@ def varz():
return generate_latest()


@app.route("/static/<path:path>", methods=["GET"])
@blueprint.route("/static/<path:path>", methods=["GET"])
def static(path: str):
return send_from_directory(static_folder, path)


@app.route("/", methods=["GET"])
@blueprint.route("/", methods=["GET"])
def get_queues():
state = KartonState(karton.backend)
return render_template("index.html", queues=state.queues)


@app.route("/services", methods=["GET"])
@blueprint.route("/services", methods=["GET"])
def get_services():
aggregated_services = defaultdict(list)
online_services = karton.backend.get_online_services()
Expand All @@ -246,7 +256,7 @@ def get_services():
return render_template("services.html", services=aggregated_services)


@app.route("/api/queues", methods=["GET"])
@blueprint.route("/api/queues", methods=["GET"])
def get_queues_api():
state = KartonState(karton.backend)
return jsonify(
Expand All @@ -257,7 +267,7 @@ def get_queues_api():
)


@app.route("/<queue_name>/restart_crashed", methods=["POST"])
@blueprint.route("/<queue_name>/restart_crashed", methods=["POST"])
def restart_crashed_queue_tasks(queue_name):
state = KartonState(karton.backend)
queue = state.queues.get(queue_name)
Expand All @@ -269,7 +279,7 @@ def restart_crashed_queue_tasks(queue_name):
return redirect(request.referrer)


@app.route("/<queue_name>/cancel_crashed", methods=["POST"])
@blueprint.route("/<queue_name>/cancel_crashed", methods=["POST"])
def cancel_crashed_queue_tasks(queue_name):
state = KartonState(karton.backend)
queue = state.queues.get(queue_name)
Expand All @@ -280,7 +290,7 @@ def cancel_crashed_queue_tasks(queue_name):
return redirect(request.referrer)


@app.route("/<queue_name>/cancel_pending", methods=["POST"])
@blueprint.route("/<queue_name>/cancel_pending", methods=["POST"])
def cancel_pending_queue_tasks(queue_name):
state = KartonState(karton.backend)
queue = state.queues.get(queue_name)
Expand All @@ -291,7 +301,7 @@ def cancel_pending_queue_tasks(queue_name):
return redirect(request.referrer)


@app.route("/restart_task/<task_id>/restart", methods=["POST"])
@blueprint.route("/restart_task/<task_id>/restart", methods=["POST"])
def restart_task(task_id):
task = karton.backend.get_task(task_id)
if not task:
Expand All @@ -301,7 +311,7 @@ def restart_task(task_id):
return redirect(request.referrer)


@app.route("/cancel_task/<task_id>/cancel", methods=["POST"])
@blueprint.route("/cancel_task/<task_id>/cancel", methods=["POST"])
def cancel_task(task_id):
task = karton.backend.get_task(task_id)
if not task:
Expand All @@ -311,7 +321,7 @@ def cancel_task(task_id):
return redirect(request.referrer)


@app.route("/queue/<queue_name>", methods=["GET"])
@blueprint.route("/queue/<queue_name>", methods=["GET"])
def get_queue(queue_name):
state = KartonState(karton.backend)
queue = state.queues.get(queue_name)
Expand All @@ -321,7 +331,7 @@ def get_queue(queue_name):
return render_template("queue.html", name=queue_name, queue=queue)


@app.route("/queue/<queue_name>/crashed", methods=["GET"])
@blueprint.route("/queue/<queue_name>/crashed", methods=["GET"])
def get_crashed_queue(queue_name):
state = KartonState(karton.backend)
queue = state.queues.get(queue_name)
Expand All @@ -331,7 +341,7 @@ def get_crashed_queue(queue_name):
return render_template("crashed.html", name=queue_name, queue=queue)


@app.route("/api/queue/<queue_name>", methods=["GET"])
@blueprint.route("/api/queue/<queue_name>", methods=["GET"])
def get_queue_api(queue_name):
state = KartonState(karton.backend)
queue = state.queues.get(queue_name)
Expand All @@ -340,7 +350,7 @@ def get_queue_api(queue_name):
return jsonify(QueueView(queue).to_dict())


@app.route("/task/<task_id>", methods=["GET"])
@blueprint.route("/task/<task_id>", methods=["GET"])
def get_task(task_id):
task = karton.backend.get_task(task_id)
if not task:
Expand All @@ -351,15 +361,15 @@ def get_task(task_id):
)


@app.route("/api/task/<task_id>", methods=["GET"])
@blueprint.route("/api/task/<task_id>", methods=["GET"])
def get_task_api(task_id):
task = karton.backend.get_task(task_id)
if not task:
return jsonify({"error": "Task doesn't exist"}), 404
return jsonify(TaskView(task).to_dict())


@app.route("/analysis/<root_id>", methods=["GET"])
@blueprint.route("/analysis/<root_id>", methods=["GET"])
def get_analysis(root_id):
state = KartonState(karton.backend)
analysis = state.analyses.get(root_id)
Expand All @@ -371,7 +381,7 @@ def get_analysis(root_id):
)


@app.route("/api/analysis/<root_id>", methods=["GET"])
@blueprint.route("/api/analysis/<root_id>", methods=["GET"])
def get_analysis_api(root_id):
state = KartonState(karton.backend)
analysis = state.analyses.get(root_id)
Expand All @@ -381,15 +391,18 @@ def get_analysis_api(root_id):
return jsonify(AnalysisView(analysis).to_dict())


@app.route("/graph", methods=["GET"])
@blueprint.route("/graph", methods=["GET"])
def get_graph():
return render_template("graph.html")


@app.route("/graph/generate", methods=["GET"])
@blueprint.route("/graph/generate", methods=["GET"])
def generate_graph():
state = KartonState(karton.backend)
graph = KartonGraph(state)
raw_graph = graph.generate_graph()

return raw_graph


app.register_blueprint(blueprint, url_prefix=base_path)
2 changes: 1 addition & 1 deletion karton/dashboard/static/graph/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ window.onresize = function() {

option = null;
myChart.showLoading('default', loadingOpts);
$.get("/graph/generate", function(raw_graph) {
$.get(dom.getAttribute('data-generate-url'), function(raw_graph) {
myChart.hideLoading();
myChart.resize();

Expand Down
4 changes: 2 additions & 2 deletions karton/dashboard/templates/analysis.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ <h4>Tasks</h4>
{% for task in queue.pending_tasks %}
<tr>
<td>
<a href="/queue/{{identity}}">{{identity}}</a>
<a href="{{ url_for('dashboard.get_queue', queue_name=identity) }}">{{identity}}</a>
</td>
<td>
<a href="/task/{{task.uid}}">{{ task.uid }}</a>
<a href="{{ url_for('dashboard.get_task', task_id=task.uid) }}">{{ task.uid }}</a>
</td>
<td>
{% if task.priority.value != 'normal' %}
Expand Down
10 changes: 5 additions & 5 deletions karton/dashboard/templates/crashed.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ <h4>
<span class="align-middle">Crashed tasks</span>
{% if queue.crashed_tasks %}
<div class="btn-group float-right">
<form action={{url_for("restart_crashed_queue_tasks", queue_name=name)}} method="POST">
<form action={{url_for('dashboard.restart_crashed_queue_tasks', queue_name=name)}} method="POST">
<button class="btn btn-sm btn-danger mx-1" type="submit" value="Submit" title="restart all tasks">Restart all</button>
</form>
<div class="divider"></div>
<form action={{url_for("cancel_crashed_queue_tasks", queue_name=name)}} method="POST" id="cancelQueue">
<form action={{url_for('dashboard.cancel_crashed_queue_tasks', queue_name=name)}} method="POST" id="cancelQueue">
<button class="btn btn-sm btn-danger" type="submit" form="cancelQueue" value="Submit" title="cancel all tasks">Cancel all</button>
</form>
</div>
Expand All @@ -28,7 +28,7 @@ <h4>
{% for task in queue.crashed_tasks|sort(attribute='last_update', reverse=True) %}
<tr>
<td>
<a href="/task/{{ task.uid }}">{{ task.uid }}</a>
<a href="{{url_for('dashboard.get_task', task_id=task.uid)}}">{{ task.uid }}</a>
</td>
<td><pre>{{ task.last_update|render_timestamp }}</pre></td>
<td>
Expand All @@ -54,15 +54,15 @@ <h4>
</td>
<td>
<div class="btn-group">
<form action={{url_for("restart_task", task_id=task.uid)}} method="POST">
<form action={{url_for('dashboard.restart_task', task_id=task.uid)}} method="POST">
<button class="btn btn-info mx-1" type="submit" value="Submit" title="restart task">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>
</button>
</form>
<form action={{url_for("cancel_task", task_id=task.uid)}} method="POST">
<form action={{url_for('dashboard.cancel_task', task_id=task.uid)}} method="POST">
<button class="btn btn-danger" type="submit" value="Submit" title="cancel task">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
<path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1H2.5zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zM8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5zm3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0z"/>
Expand Down
6 changes: 3 additions & 3 deletions karton/dashboard/templates/graph.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends 'layout.html' %}
{% block content %}
<div class="bs-component">
<link type="text/css" rel="stylesheet" href="../static/graph/style.css" />
<link rel="stylesheet" href="{{ url_for('dashboard.static', path='graph/style.css') }}">
<script
type="text/javascript"
src="https://code.jquery.com/jquery-3.4.1.min.js"
Expand Down Expand Up @@ -30,7 +30,7 @@
</div>
</div>

<div id="graph-container" style="height: 100vh;"></div>
<script src="../static/graph/graph.js"></script>
<div id="graph-container" data-generate-url="{{ url_for('dashboard.generate_graph') }}" style="height: 100vh;"></div>
<script src="{{ url_for('dashboard.static', path='graph/graph.js') }}"></script>
</div>
{% endblock %}
4 changes: 2 additions & 2 deletions karton/dashboard/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ <h3 class="text-center">binds</h3>
{% for (queue_name, queue) in queues|dictsort %}
<tr>
<td>
<a href="queue/{{ queue_name }}">{{ queue_name }}</a>
<a href="{{url_for('dashboard.get_queue', queue_name=queue_name)}}">{{ queue_name }}</a>
<div>
<span class="badge bg-info" title="karton-core library version">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-seam" viewBox="0 0 16 16">
Expand Down Expand Up @@ -67,7 +67,7 @@ <h3 class="text-center">binds</h3>
</td>
<td>
{% set length = queue.crashed_tasks | length %}
{% set url = url_for("get_crashed_queue", queue_name=queue_name) %}
{% set url = url_for('dashboard.get_crashed_queue', queue_name=queue_name) %}
{% set badgeClass = "bg-success" if length == 0 else "bg-danger" %}
<span class="badge {{badgeClass}}">
<a class="text-decoration-none" href={{url}} style="color: inherit">{{length}}</a>
Expand Down
12 changes: 6 additions & 6 deletions karton/dashboard/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>{% block title %}karton-dashboard{% endblock %}</title>
<link rel="stylesheet" href="/static/bootstrap.css">
<link rel="shortcut icon" href="{{ url_for('static', path='favicon.ico') }}">
<link rel="stylesheet" href="{{ url_for('dashboard.static', path='bootstrap.css') }}">
<link rel="shortcut icon" href="{{ url_for('dashboard.static', path='favicon.ico') }}">
</head>

<body>
<div class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a href="/" class="navbar-brand">karton</a>
<a href="{{ url_for('dashboard.get_queues') }}" class="navbar-brand">karton</a>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/">home</a>
<a class="nav-link" href="{{ url_for('dashboard.get_queues') }}">home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/services">services</a>
<a class="nav-link" href="{{ url_for('dashboard.get_services') }}">services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/graph">graph</a>
<a class="nav-link" href="{{ url_for('dashboard.get_graph') }}">graph</a>
</li>
</ul>
</div>
Expand Down
6 changes: 3 additions & 3 deletions karton/dashboard/templates/queue.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ <h4>
<span class="align-middle">Tasks</span>
{% if queue.pending_tasks %}
<div class="btn-group float-right">
<form action={{url_for("cancel_pending_queue_tasks", queue_name=name)}} method="POST" id="cancelQueue">
<form action={{url_for('dashboard.cancel_pending_queue_tasks', queue_name=name)}} method="POST" id="cancelQueue">
<button class="btn btn-sm btn-secondary" type="submit" form="cancelQueue" value="Submit" title="cancel all tasks">Cancel all</button>
</form>
</div>
Expand All @@ -23,7 +23,7 @@ <h4>
{% for task in queue.pending_tasks|sort(attribute='last_update', reverse=True) %}
<tr>
<td>
<a href="/task/{{task.uid}}">{{ task.uid }}</a>
<a href="{{ url_for('dashboard.get_task', task_id=task.uid) }}">{{ task.uid }}</a>
</td>
<td><pre>{{ task.last_update|render_timestamp }}</pre></td>
<td>
Expand All @@ -46,7 +46,7 @@ <h4>
</td>
<td>
<div class="btn-group">
<form action={{url_for("cancel_task", task_id=task.uid)}} method="POST">
<form action={{url_for('dashboard.cancel_task', task_id=task.uid)}} method="POST">
<button class="btn btn-dark" type="submit" value="Submit" title="cancel task"></button>
</form>
</div>
Expand Down
Loading

0 comments on commit 8b2a11c

Please sign in to comment.