From f43fde017581146fdbf71d6c3b1a0dbc370609ee Mon Sep 17 00:00:00 2001 From: Vicente Olivert Riera Date: Mon, 1 Jul 2024 15:46:49 +0900 Subject: [PATCH] Move promql-query request to the backend The API end point of some shards are protected with HTTP Basic Auth. This is making the promql-query component to fail receiving a '401 Unauthorized' error response. Now that the Shard model supports an "authorization" field to specify the value of the "Authorization" HTTP header, we can address this issue. However, if we perform the request in the front end, that information would be visible by a simple inspection of the HTTP headers. Because of that we have moved it to the back end. --- promgen/static/js/promgen.vue.js | 10 ++++---- promgen/templates/promgen/project_form.html | 4 ++-- promgen/templates/promgen/shard_header.html | 4 ++-- promgen/urls.py | 2 ++ promgen/views.py | 26 +++++++++++++++++++++ 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/promgen/static/js/promgen.vue.js b/promgen/static/js/promgen.vue.js index 77679d3d5..45c726a41 100644 --- a/promgen/static/js/promgen.vue.js +++ b/promgen/static/js/promgen.vue.js @@ -185,7 +185,7 @@ app.component('silence-form', { app.component("promql-query", { delimiters: ['[[', ']]'], - props: ["href", "query", "max"], + props: ["shard", "query", "max"], data: function () { return { count: 0, @@ -207,9 +207,11 @@ app.component("promql-query", { }, template: '#promql-query-template', mounted() { - var url = new URL(this.href); - url.search = new URLSearchParams({ query: this.query }); - fetch(url) + const params = new URLSearchParams({ + shard: this.shard, + query: this.query, + }); + fetch(`/promql-query?${params}`) .then(response => response.json()) .then(result => this.count = Number.parseInt(result.data.result[0].value[1])) .finally(() => this.ready = true); diff --git a/promgen/templates/promgen/project_form.html b/promgen/templates/promgen/project_form.html index edf758a03..15cad6127 100644 --- a/promgen/templates/promgen/project_form.html +++ b/promgen/templates/promgen/project_form.html @@ -87,10 +87,10 @@

Register new Project

{% if shard.proxy %} - Samples: + Samples: - Exporters: + Exporters: {% else %}   diff --git a/promgen/templates/promgen/shard_header.html b/promgen/templates/promgen/shard_header.html index e98d03c2f..89fe4e49c 100644 --- a/promgen/templates/promgen/shard_header.html +++ b/promgen/templates/promgen/shard_header.html @@ -1,8 +1,8 @@ {% load i18n %}
{% if shard.proxy %} - Samples: - Exporters: + Samples: + Exporters: {% endif %} {% if user.is_superuser %} diff --git a/promgen/urls.py b/promgen/urls.py index b227b2ca1..f1c1f6dd0 100644 --- a/promgen/urls.py +++ b/promgen/urls.py @@ -126,6 +126,8 @@ path("proxy/v1/silences/", csrf_exempt(proxy.ProxyDeleteSilence.as_view()), name="proxy-silence-delete"), # Promgen rest API path("rest/", include((router.urls, "api"), namespace="api")), + # PromQL Query + path("promql-query", views.PromqlQuery.as_view(), name="promql-query"), ] try: diff --git a/promgen/views.py b/promgen/views.py index 53963c8f2..9c0bb86f7 100644 --- a/promgen/views.py +++ b/promgen/views.py @@ -28,6 +28,7 @@ from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import CreateView, DeleteView, FormView from prometheus_client.core import CounterMetricFamily, GaugeMetricFamily +from requests.exceptions import HTTPError import promgen.templatetags.promgen as macro from promgen import ( @@ -1374,3 +1375,28 @@ def post(self, request, pk): return JsonResponse( {request.POST["target"]: render_to_string("promgen/ajax_clause_check.html", result)} ) + + +class PromqlQuery(View): + def get(self, request): + if not all(x in request.GET for x in ["shard", "query"]): + return HttpResponse("BAD REQUEST", status=400) + + shard = models.Shard.objects.get(pk=request.GET["shard"]) + params = {"query": request.GET["query"]} + headers = {} + + if shard.authorization: + headers["Authorization"] = shard.authorization + + try: + response = util.get(f"{shard.url}/api/v1/query", params=params, headers=headers) + response.raise_for_status() + except HTTPError: + return HttpResponse( + response.content, + content_type=response.headers["content-type"], + status=response.status_code, + ) + + return HttpResponse(response.content, content_type="application/json")