From 790b9f6dbfd0db6f0f16970f1103a473ddb3ccf8 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 28 Sep 2020 10:23:11 -0400 Subject: [PATCH] revert cleanup Signed-off-by: Sebastian Malton --- src/main/cluster.ts | 14 ++-- src/main/routes/metrics-route.ts | 109 +++++++++++++++---------------- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 0bbb9ed0f6..12acb793ab 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -230,12 +230,16 @@ export class Cluster implements ClusterModel { } protected async k8sRequest(path: string, options: RequestPromiseOptions = {}): Promise { - (options.headers ??= {}).Host = `${this.id}.${new URL(this.kubeProxyUrl).host}` // required in ClusterManager.getClusterForRequest() - options.json ??= true - options.timeout ??= 5000 - const apiUrl = this.kubeProxyUrl + path; - return request(apiUrl, options) + return request(apiUrl, { + json: true, + timeout: 5000, + ...options, + headers: { + Host: `${this.id}.${new URL(this.kubeProxyUrl).host}`, // required in ClusterManager.getClusterForRequest() + ...(options.headers || {}), + }, + }) } getMetrics(prometheusPath: string, queryParams: IMetricsReqParams & { query: string }) { diff --git a/src/main/routes/metrics-route.ts b/src/main/routes/metrics-route.ts index 1c39fd6c1d..2665fca5f2 100644 --- a/src/main/routes/metrics-route.ts +++ b/src/main/routes/metrics-route.ts @@ -1,73 +1,72 @@ import { LensApiRequest } from "../router" import { LensApi } from "../lens-api" -import { PrometheusProvider } from "../prometheus/provider-registry" -import { Cluster } from "../cluster"; +import { PrometheusClusterQuery, PrometheusIngressQuery, PrometheusNodeQuery, PrometheusPodQuery, PrometheusProvider, PrometheusPvcQuery, PrometheusQueryOpts } from "../prometheus/provider-registry" export type IMetricsQuery = string | string[] | { [metricName: string]: string; } -// This is used for backoff retry tracking. -const MAX_ATTEMPTS = 5 -const ATTEMPTS = (() => { const t = []; t[MAX_ATTEMPTS] = true; return t; })() - -// prometheus metrics loader -async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record): Promise { - const queries = promQueries.map(p => p.trim()) - const loaders = new Map>() - - async function loadMetric(query: string): Promise { - async function loadMetricHelper(): Promise { - for (const [attempt, lastAttempt] of ATTEMPTS.entries()) { // retry - try { - return await cluster.getMetrics(prometheusPath, { query, ...queryParams }) - } catch (error) { - if (lastAttempt || error?.statusCode === 404) { - return { - status: error.toString(), - data: { result: [] }, - } - } - - await new Promise(resolve => setTimeout(resolve, attempt * 1000)); // add delay before repeating request - } - } - } - - return loaders.get(query) ?? loaders.set(query, loadMetricHelper()).get(query) - } - - return Promise.all(queries.map(loadMetric)) -} - class MetricsRoute extends LensApi { - async routeMetrics({ response, cluster, payload, query }: LensApiRequest) { - const queryParams: IMetricsQuery = Object.fromEntries(query.entries()) - + async routeMetrics(request: LensApiRequest) { + const { response, cluster, payload } = request + const queryParams: IMetricsQuery = {} + request.query.forEach((value: string, key: string) => { + queryParams[key] = value + }) + let prometheusPath: string + let prometheusProvider: PrometheusProvider try { - const [prometheusPath, prometheusProvider] = await Promise.all([ + [prometheusPath, prometheusProvider] = await Promise.all([ cluster.contextHandler.getPrometheusPath(), cluster.contextHandler.getPrometheusProvider() ]) - - // return data in same structure as query - if (typeof payload === "string") { - const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams) - this.respondJson(response, data) - } else if (Array.isArray(payload)) { - const data = await loadMetrics(payload, cluster, prometheusPath, queryParams) - this.respondJson(response, data) - } else { - const queries = Object.entries(payload).map(([queryName, queryOpts]) => ( - (prometheusProvider.getQueries(queryOpts) as Record)[queryName] - )) - const result = await loadMetrics(queries, cluster, prometheusPath, queryParams) - const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]])) - this.respondJson(response, data) - } } catch { this.respondJson(response, {}) + return } + // prometheus metrics loader + const attempts: { [query: string]: number } = {}; + const maxAttempts = 5; + const loadMetrics = (promQuery: string): Promise => { + const query = promQuery.trim() + const attempt = attempts[query] = (attempts[query] || 0) + 1; + return cluster.getMetrics(prometheusPath, { query, ...queryParams }).catch(async error => { + if (attempt < maxAttempts && (error.statusCode && error.statusCode != 404)) { + await new Promise(resolve => setTimeout(resolve, attempt * 1000)); // add delay before repeating request + return loadMetrics(query); + } + return { + status: error.toString(), + data: { + result: [] + } + } + }) + }; + + // return data in same structure as query + let data: any; + if (typeof payload === "string") { + data = await loadMetrics(payload) + } else if (Array.isArray(payload)) { + data = await Promise.all(payload.map(loadMetrics)); + } else { + data = {}; + const result = await Promise.all( + Object.entries(payload).map((queryEntry: any) => { + const queryName: string = queryEntry[0] + const queryOpts: PrometheusQueryOpts = queryEntry[1] + const queries = prometheusProvider.getQueries(queryOpts) + const q = queries[queryName as keyof (PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery)] + return loadMetrics(q) + }) + ); + Object.keys(payload).forEach((metricName, index) => { + data[metricName] = result[index]; + }); + } + + this.respondJson(response, data) } }