1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Store prometheus metadata for clusters based on metrics requests

Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
Lauri Nevala 2020-11-19 10:41:37 +02:00
parent 04517148c0
commit 2e568d3413
4 changed files with 31 additions and 18 deletions

View File

@ -21,7 +21,13 @@ export interface ClusterIconUpload {
} }
export interface ClusterMetadata { export interface ClusterMetadata {
[key: string]: string | number | boolean; [key: string]: string | number | boolean | object;
}
export type ClusterPrometheusMetadata = {
success?: boolean;
provider?: string;
autoDetected?: boolean;
} }
export interface ClusterStoreModel { export interface ClusterStoreModel {

View File

@ -139,6 +139,7 @@ export class Cluster implements ClusterModel, ClusterState {
if (ipcMain) { if (ipcMain) {
this.eventDisposers.push( this.eventDisposers.push(
reaction(() => this.getState(), () => this.pushState()), reaction(() => this.getState(), () => this.pushState()),
reaction(() => this.preferences, () => this.contextHandler.setupPrometheus(this.preferences)),
() => { () => {
clearInterval(refreshTimer) clearInterval(refreshTimer)
clearInterval(refreshMetadataTimer) clearInterval(refreshMetadataTimer)

View File

@ -22,7 +22,7 @@ export class ContextHandler {
this.setupPrometheus(cluster.preferences); this.setupPrometheus(cluster.preferences);
} }
protected setupPrometheus(preferences: ClusterPreferences = {}) { public setupPrometheus(preferences: ClusterPreferences = {}) {
this.prometheusProvider = preferences.prometheusProvider?.type; this.prometheusProvider = preferences.prometheusProvider?.type;
this.prometheusPath = null; this.prometheusPath = null;
if (preferences.prometheus) { if (preferences.prometheus) {
@ -32,13 +32,18 @@ export class ContextHandler {
} }
protected async resolvePrometheusPath(): Promise<string> { protected async resolvePrometheusPath(): Promise<string> {
const { service, namespace, port } = await this.getPrometheusService() const prometheusService = await this.getPrometheusService()
if (!prometheusService) return null
const { service, namespace, port } = prometheusService
return `${namespace}/services/${service}:${port}` return `${namespace}/services/${service}:${port}`
} }
async getPrometheusProvider() { async getPrometheusProvider() {
if (!this.prometheusProvider) { if (!this.prometheusProvider) {
const service = await this.getPrometheusService() const service = await this.getPrometheusService()
if (!service) {
return null
}
logger.info(`using ${service.id} as prometheus provider`) logger.info(`using ${service.id} as prometheus provider`)
this.prometheusProvider = service.id this.prometheusProvider = service.id
} }
@ -52,13 +57,7 @@ export class ContextHandler {
return await provider.getPrometheusService(apiClient) return await provider.getPrometheusService(apiClient)
}) })
const resolvedPrometheusServices = await Promise.all(prometheusPromises) const resolvedPrometheusServices = await Promise.all(prometheusPromises)
const service = resolvedPrometheusServices.filter(n => n)[0]; return resolvedPrometheusServices.filter(n => n)[0];
return service || {
id: "lens",
namespace: "lens-metrics",
service: "prometheus",
port: 80
}
} }
async getPrometheusPath(): Promise<string> { async getPrometheusPath(): Promise<string> {

View File

@ -2,6 +2,7 @@ import { LensApiRequest } from "../router"
import { LensApi } from "../lens-api" import { LensApi } from "../lens-api"
import { Cluster } from "../cluster" import { Cluster } from "../cluster"
import _ from "lodash" import _ from "lodash"
import { ClusterPrometheusMetadata } from "../../common/cluster-store"
export type IMetricsQuery = string | string[] | { export type IMetricsQuery = string | string[] | {
[metricName: string]: string; [metricName: string]: string;
@ -22,11 +23,8 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa
try { try {
return await cluster.getMetrics(prometheusPath, { query, ...queryParams }) return await cluster.getMetrics(prometheusPath, { query, ...queryParams })
} catch (error) { } catch (error) {
if (lastAttempt || error?.statusCode === 404) { if (lastAttempt || (error?.statusCode >= 400 && error?.statusCode < 500)) {
return { throw new Error("Metrics not available");
status: error.toString(),
data: { result: [] },
}
} }
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request
@ -36,20 +34,25 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa
return loaders.get(query) ?? loaders.set(query, loadMetricHelper()).get(query) return loaders.get(query) ?? loaders.set(query, loadMetricHelper()).get(query)
} }
return Promise.all(queries.map(loadMetric)) return Promise.all(queries.map(loadMetric))
} }
class MetricsRoute extends LensApi { class MetricsRoute extends LensApi {
async routeMetrics({ response, cluster, payload, query }: LensApiRequest) { async routeMetrics({ response, cluster, payload, query }: LensApiRequest) {
const queryParams: IMetricsQuery = Object.fromEntries(query.entries()) const queryParams: IMetricsQuery = Object.fromEntries(query.entries())
const prometheusMetadata: ClusterPrometheusMetadata = {}
try { try {
const [prometheusPath, prometheusProvider] = await Promise.all([ const [prometheusPath, prometheusProvider] = await Promise.all([
cluster.contextHandler.getPrometheusPath(), cluster.contextHandler.getPrometheusPath(),
cluster.contextHandler.getPrometheusProvider() cluster.contextHandler.getPrometheusProvider()
]) ])
prometheusMetadata["provider"] = prometheusProvider?.id
prometheusMetadata["autoDetected"] = !cluster.preferences.prometheusProvider?.type
if (!prometheusPath) {
prometheusMetadata["success"] = false
this.respondJson(response, {})
return
}
// return data in same structure as query // return data in same structure as query
if (typeof payload === "string") { if (typeof payload === "string") {
const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams) const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams)
@ -65,8 +68,12 @@ class MetricsRoute extends LensApi {
const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]])) const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]]))
this.respondJson(response, data) this.respondJson(response, data)
} }
prometheusMetadata["success"] = true
} catch { } catch {
prometheusMetadata["success"] = false
this.respondJson(response, {}) this.respondJson(response, {})
} finally {
cluster.metadata["prometheus"] = prometheusMetadata
} }
} }
} }