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

Allow to select Prometheus query style (#312)

Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
Lauri Nevala 2020-05-01 09:37:05 +03:00 committed by GitHub
parent 29cd171d0f
commit 724cf0c687
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 333 additions and 90 deletions

View File

@ -5,37 +5,21 @@ import { KubeApi } from "../kube-api";
export class ClusterApi extends KubeApi<Cluster> { export class ClusterApi extends KubeApi<Cluster> {
async getMetrics(nodeNames: string[], params?: IMetricsReqParams): Promise<IClusterMetrics> { async getMetrics(nodeNames: string[], params?: IMetricsReqParams): Promise<IClusterMetrics> {
const nodes = nodeNames.join("|"); const nodes = nodeNames.join("|");
const memoryUsage = ` const opts = { category: "cluster", nodes: nodes }
sum(
node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)
) by (kubernetes_name)
`.replace(/_bytes/g, `_bytes{kubernetes_node=~"${nodes}"}`);
const memoryRequests = `sum(kube_pod_container_resource_requests{node=~"${nodes}", resource="memory"}) by (component)`;
const memoryLimits = `sum(kube_pod_container_resource_limits{node=~"${nodes}", resource="memory"}) by (component)`;
const memoryCapacity = `sum(kube_node_status_capacity{node=~"${nodes}", resource="memory"}) by (component)`;
const cpuUsage = `sum(rate(node_cpu_seconds_total{kubernetes_node=~"${nodes}", mode=~"user|system"}[1m]))`;
const cpuRequests = `sum(kube_pod_container_resource_requests{node=~"${nodes}", resource="cpu"}) by (component)`;
const cpuLimits = `sum(kube_pod_container_resource_limits{node=~"${nodes}", resource="cpu"}) by (component)`;
const cpuCapacity = `sum(kube_node_status_capacity{node=~"${nodes}", resource="cpu"}) by (component)`;
const podUsage = `sum(kubelet_running_pod_count{instance=~"${nodes}"})`;
const podCapacity = `sum(kube_node_status_capacity{node=~"${nodes}", resource="pods"}) by (component)`;
const fsSize = `sum(node_filesystem_size_bytes{kubernetes_node=~"${nodes}", mountpoint="/"}) by (kubernetes_node)`;
const fsUsage = `sum(node_filesystem_size_bytes{kubernetes_node=~"${nodes}", mountpoint="/"} - node_filesystem_avail_bytes{kubernetes_node=~"${nodes}", mountpoint="/"}) by (kubernetes_node)`;
return metricsApi.getMetrics({ return metricsApi.getMetrics({
memoryUsage, memoryUsage: opts,
memoryRequests, memoryRequests: opts,
memoryLimits, memoryLimits: opts,
memoryCapacity, memoryCapacity: opts,
cpuUsage, cpuUsage: opts,
cpuRequests, cpuRequests: opts,
cpuLimits, cpuLimits: opts,
cpuCapacity, cpuCapacity: opts,
podUsage, podUsage: opts,
podCapacity, podCapacity: opts,
fsSize, fsSize: opts,
fsUsage fsUsage: opts
}, params); }, params);
} }
} }

View File

@ -5,17 +5,12 @@ import { KubeApi } from "../kube-api";
export class IngressApi extends KubeApi<Ingress> { export class IngressApi extends KubeApi<Ingress> {
getMetrics(ingress: string, namespace: string): Promise<IIngressMetrics> { getMetrics(ingress: string, namespace: string): Promise<IIngressMetrics> {
const bytesSent = (statuses: string) => const opts = { category: "ingress", ingress }
`sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[1m])) by (ingress)`;
const bytesSentSuccess = bytesSent("^2\\\\d*"); // Requests with status 2**
const bytesSentFailure = bytesSent("^5\\\\d*"); // Requests with status 5**
const requestDurationSeconds = `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${ingress}"}[1m])) by (ingress)`;
const responseDurationSeconds = `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${ingress}"}[1m])) by (ingress)`;
return metricsApi.getMetrics({ return metricsApi.getMetrics({
bytesSentSuccess, bytesSentSuccess: opts,
bytesSentFailure, bytesSentFailure: opts,
requestDurationSeconds, requestDurationSeconds: opts,
responseDurationSeconds responseDurationSeconds: opts
}, { }, {
namespace, namespace,
}); });

View File

@ -5,24 +5,15 @@ import { KubeApi } from "../kube-api";
export class NodesApi extends KubeApi<Node> { export class NodesApi extends KubeApi<Node> {
getMetrics(): Promise<INodeMetrics> { getMetrics(): Promise<INodeMetrics> {
const memoryUsage = ` const opts = { category: "nodes"}
sum (
node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)
) by (kubernetes_node)
`;
const memoryCapacity = `sum(kube_node_status_capacity{resource="memory"}) by (node)`;
const cpuUsage = `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[1m])) by(kubernetes_node)`;
const cpuCapacity = `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`;
const fsSize = `sum(node_filesystem_size_bytes{mountpoint="/"}) by (kubernetes_node)`;
const fsUsage = `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (kubernetes_node)`;
return metricsApi.getMetrics({ return metricsApi.getMetrics({
memoryUsage, memoryUsage: opts,
memoryCapacity, memoryCapacity: opts,
cpuUsage, cpuUsage: opts,
cpuCapacity, cpuCapacity: opts,
fsSize, fsSize: opts,
fsUsage fsUsage: opts
}); });
} }
} }

View File

@ -6,12 +6,9 @@ import { KubeApi } from "../kube-api";
export class PersistentVolumeClaimsApi extends KubeApi<PersistentVolumeClaim> { export class PersistentVolumeClaimsApi extends KubeApi<PersistentVolumeClaim> {
getMetrics(pvcName: string, namespace: string): Promise<IPvcMetrics> { getMetrics(pvcName: string, namespace: string): Promise<IPvcMetrics> {
const diskUsage = `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${pvcName}"}) by (persistentvolumeclaim, namespace)`;
const diskCapacity = `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${pvcName}"}) by (persistentvolumeclaim, namespace)`;
return metricsApi.getMetrics({ return metricsApi.getMetrics({
diskUsage, diskUsage: { category: 'pvc', pvc: pvcName },
diskCapacity diskCapacity: { category: 'pvc', pvc: pvcName }
}, { }, {
namespace namespace
}); });

View File

@ -11,26 +11,18 @@ export class PodsApi extends KubeApi<Pod> {
getMetrics(pods: Pod[], namespace: string, selector = "pod, namespace"): Promise<IPodMetrics> { getMetrics(pods: Pod[], namespace: string, selector = "pod, namespace"): Promise<IPodMetrics> {
const podSelector = pods.map(pod => pod.getName()).join("|"); const podSelector = pods.map(pod => pod.getName()).join("|");
const cpuUsage = `sum(rate(container_cpu_usage_seconds_total{container_name!="POD",container_name!="",pod_name=~"${podSelector}",namespace="${namespace}"}[1m])) by (${selector})`; const opts = { category: "pods", pods: podSelector, namespace, selector }
const cpuRequests = `sum(kube_pod_container_resource_requests{pod=~"${podSelector}",resource="cpu",namespace="${namespace}"}) by (${selector})`;
const cpuLimits = `sum(kube_pod_container_resource_limits{pod=~"${podSelector}",resource="cpu",namespace="${namespace}"}) by (${selector})`;
const memoryUsage = `sum(container_memory_working_set_bytes{container_name!="POD",container_name!="",pod_name=~"${podSelector}",namespace="${namespace}"}) by (${selector})`;
const memoryRequests = `sum(kube_pod_container_resource_requests{pod=~"${podSelector}",resource="memory",namespace="${namespace}"}) by (${selector})`;
const memoryLimits = `sum(kube_pod_container_resource_limits{pod=~"${podSelector}",resource="memory",namespace="${namespace}"}) by (${selector})`;
const fsUsage = `sum(container_fs_usage_bytes{container_name!="POD",container_name!="",pod_name=~"${podSelector}",namespace="${namespace}"}) by (${selector})`;
const networkReceive = `sum(rate(container_network_receive_bytes_total{pod_name=~"${podSelector}",namespace="${namespace}"}[1m])) by (${selector})`;
const networkTransit = `sum(rate(container_network_transmit_bytes_total{pod_name=~"${podSelector}",namespace="${namespace}"}[1m])) by (${selector})`;
return metricsApi.getMetrics({ return metricsApi.getMetrics({
cpuUsage, cpuUsage: opts,
cpuRequests, cpuRequests: opts,
cpuLimits, cpuLimits: opts,
memoryUsage, memoryUsage: opts,
memoryRequests, memoryRequests: opts,
memoryLimits, memoryLimits: opts,
fsUsage, fsUsage: opts,
networkReceive, networkReceive: opts,
networkTransit, networkTransit: opts,
}, { }, {
namespace, namespace,
}); });

View File

@ -1,4 +1,4 @@
export type IMetricsQuery = string | string[] | { export type IMetricsQuery = string | string[] | {
[metricName: string]: string; [metricName: string]: string | object;
} }

View File

@ -0,0 +1,11 @@
import { PrometheusLens } from "../main/prometheus/lens";
import { PrometheusHelm } from "../main/prometheus/helm";
import { PrometheusOperator } from "../main/prometheus/operator";
import { PrometheusProviderRegistry } from "../main/prometheus/provider-registry";
[PrometheusLens, PrometheusHelm, PrometheusOperator].forEach(providerClass => {
const provider = new providerClass()
PrometheusProviderRegistry.registerProvider(provider.id, provider)
});
export const prometheusProviders = PrometheusProviderRegistry.getProviders()

View File

@ -47,6 +47,9 @@ export type ClusterPreferences = {
service: string; service: string;
port: number; port: number;
}; };
prometheusProvider?: {
type: string;
};
icon?: string; icon?: string;
httpsProxy?: string; httpsProxy?: string;
} }

View File

@ -15,7 +15,7 @@ import { shellSync } from "./shell-sync"
import { getFreePort } from "./port" import { getFreePort } from "./port"
import { mangleProxyEnv } from "./proxy-env" import { mangleProxyEnv } from "./proxy-env"
import { findMainWebContents } from "./webcontents" import { findMainWebContents } from "./webcontents"
import { helmCli } from "./helm-cli" import "../common/prometheus-providers"
mangleProxyEnv() mangleProxyEnv()
if (app.commandLine.getSwitchValue("proxy-server") !== "") { if (app.commandLine.getSwitchValue("proxy-server") !== "") {

View File

@ -0,0 +1,10 @@
import { PrometheusLens } from "./lens"
export class PrometheusHelm extends PrometheusLens {
constructor() {
super()
this.id = "helm"
this.name = "Helm"
this.rateAccuracy = "5m"
}
}

View File

@ -0,0 +1,66 @@
import { PrometheusProvider, PrometheusQueryOpts, PrometheusClusterQuery, PrometheusNodeQuery, PrometheusPodQuery, PrometheusPvcQuery, PrometheusIngressQuery } from "./provider-registry";
export class PrometheusLens implements PrometheusProvider {
id = "lens"
name = "Lens"
rateAccuracy = "1m"
public getQueries(opts: PrometheusQueryOpts): PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery {
switch(opts.category) {
case 'cluster':
return {
memoryUsage: `
sum(
node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)
) by (kubernetes_name)
`.replace(/_bytes/g, `_bytes{kubernetes_node=~"${opts.nodes}"}`),
memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"}) by (component)`,
memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"}) by (component)`,
memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"}) by (component)`,
cpuUsage: `sum(rate(node_cpu_seconds_total{kubernetes_node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`,
cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
podUsage: `sum(kubelet_running_pod_count{instance=~"${opts.nodes}"})`,
podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"}) by (component)`,
fsSize: `sum(node_filesystem_size_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"}) by (kubernetes_node)`,
fsUsage: `sum(node_filesystem_size_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{kubernetes_node=~"${opts.nodes}", mountpoint="/"}) by (kubernetes_node)`
}
case 'nodes':
return {
memoryUsage: `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (kubernetes_node)`,
memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`,
cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(kubernetes_node)`,
cpuCapacity: `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`,
fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"}) by (kubernetes_node)`,
fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (kubernetes_node)`
}
case 'pods':
return {
cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
cpuLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
fsUsage: `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
networkTransit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`
}
case 'pvc':
return {
diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`,
diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`
}
case 'ingress':
const bytesSent = (ingress: string, statuses: string) =>
`sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`
return {
bytesSentSuccess: bytesSent(opts.igress, "^2\\\\d*"),
bytesSentFailure: bytesSent(opts.ingres, "^5\\\\d*"),
requestDurationSeconds: `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)`,
responseDurationSeconds: `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)`
}
}
}
}

View File

@ -0,0 +1,66 @@
import { PrometheusProvider, PrometheusQueryOpts, PrometheusClusterQuery, PrometheusNodeQuery, PrometheusPodQuery, PrometheusPvcQuery, PrometheusIngressQuery } from "./provider-registry";
export class PrometheusOperator implements PrometheusProvider {
rateAccuracy = "1m"
id = "operator"
name = "Prometheus Operator"
public getQueries(opts: PrometheusQueryOpts): PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery {
switch(opts.category) {
case 'cluster':
return {
memoryUsage: `
sum(
node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)
) by (node)
`.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`),
memoryRequests: `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"}) by (component)`,
memoryLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="memory"}) by (component)`,
memoryCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="memory"}) by (component)`,
cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`,
cpuRequests:`sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
cpuLimits: `sum(kube_pod_container_resource_limits{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
cpuCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="cpu"}) by (component)`,
podUsage: `sum(kubelet_running_pod_count{node=~"${opts.nodes}"})`,
podCapacity: `sum(kube_node_status_capacity{node=~"${opts.nodes}", resource="pods"}) by (component)`,
fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`,
fsUsage: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}) by (node)`
}
case 'nodes':
return {
memoryUsage: `sum((node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`,
memoryCapacity: `sum(kube_node_status_capacity{resource="memory"}) by (node)`,
cpuUsage: `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}]) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`,
cpuCapacity: `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`,
fsSize: `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info) by (node)`,
fsUsage: `sum((node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`
}
case 'pods':
return {
cpuUsage: `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
cpuRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
cpuLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryUsage: `sum(container_memory_working_set_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryRequests: `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
memoryLimits: `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`,
fsUsage: `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`,
networkReceive: `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`,
networkTransit: `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`
}
case 'pvc':
return {
diskUsage: `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`,
diskCapacity: `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}"}) by (persistentvolumeclaim, namespace)`
}
case 'ingress':
const bytesSent = (ingress: string, statuses: string) =>
`sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}", status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress)`;
return {
bytesSentSuccess: bytesSent(opts.igress, "^2\\\\d*"),
bytesSentFailure: bytesSent(opts.ingres, "^5\\\\d*"),
requestDurationSeconds: `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)`,
responseDurationSeconds: `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}"}[${this.rateAccuracy}])) by (ingress)`
}
}
}
}

View File

@ -0,0 +1,76 @@
export type PrometheusClusterQuery = {
memoryUsage: string;
memoryRequests: string;
memoryLimits: string;
memoryCapacity: string;
cpuUsage: string;
cpuRequests: string;
cpuLimits: string;
cpuCapacity: string;
podUsage: string;
podCapacity: string;
}
export type PrometheusNodeQuery = {
memoryUsage: string;
memoryCapacity: string;
cpuUsage: string;
cpuCapacity: string;
fsSize: string;
fsUsage: string;
}
export type PrometheusPodQuery = {
memoryUsage: string;
memoryRequests: string;
memoryLimits: string;
cpuUsage: string;
cpuRequests: string;
cpuLimits: string;
fsUsage: string;
networkReceive: string;
networkTransit: string;
}
export type PrometheusPvcQuery = {
diskUsage: string;
diskCapacity: string;
}
export type PrometheusIngressQuery = {
bytesSentSuccess: string;
bytesSentFailure: string;
requestDurationSeconds: string;
responseDurationSeconds: string;
}
export type PrometheusQueryOpts = {
[key: string]: string | any;
};
export interface PrometheusProvider {
getQueries(opts: PrometheusQueryOpts): PrometheusNodeQuery | PrometheusClusterQuery | PrometheusPodQuery | PrometheusPvcQuery | PrometheusIngressQuery;
}
export type PrometheusProviderList = {
[key: string]: PrometheusProvider;
}
export class PrometheusProviderRegistry {
private static prometheusProviders: PrometheusProviderList = {}
static getProvider(type: string): PrometheusProvider {
if (!this.prometheusProviders[type]) {
throw "Unknown Prometheus provider";
}
return this.prometheusProviders[type]
}
static registerProvider(key: string, provider: PrometheusProvider) {
this.prometheusProviders[key] = provider
}
static getProviders(): PrometheusProvider[] {
return Object.values(this.prometheusProviders)
}
}

View File

@ -1,6 +1,7 @@
import { LensApiRequest } from "../router" import { LensApiRequest } from "../router"
import { LensApi } from "../lens-api" import { LensApi } from "../lens-api"
import * as requestPromise from "request-promise-native" import * as requestPromise from "request-promise-native"
import { PrometheusProviderRegistry, PrometheusProvider, PrometheusNodeQuery, PrometheusClusterQuery, PrometheusPodQuery, PrometheusPvcQuery, PrometheusIngressQuery, PrometheusQueryOpts} from "../prometheus/provider-registry"
type MetricsQuery = string | string[] | { type MetricsQuery = string | string[] | {
[metricName: string]: string; [metricName: string]: string;
@ -22,6 +23,14 @@ class MetricsRoute extends LensApi {
queryParams[key] = value queryParams[key] = value
}) })
const prometheusInstallationSource = cluster.preferences.prometheusProvider?.type || "lens"
let prometheusProvider: PrometheusProvider
try {
prometheusProvider = PrometheusProviderRegistry.getProvider(prometheusInstallationSource)
} catch {
this.respondJson(response, {})
return
}
// prometheus metrics loader // prometheus metrics loader
const attempts: { [query: string]: number } = {}; const attempts: { [query: string]: number } = {};
const maxAttempts = 5; const maxAttempts = 5;
@ -61,7 +70,13 @@ class MetricsRoute extends LensApi {
else { else {
data = {}; data = {};
const result = await Promise.all( const result = await Promise.all(
Object.values(query).map(loadMetrics) Object.entries(query).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(query).forEach((metricName, index) => { Object.keys(query).forEach((metricName, index) => {
data[metricName] = result[index]; data[metricName] = result[index];

View File

@ -31,6 +31,16 @@
@blur="onPrometheusSave" @blur="onPrometheusSave"
/> />
</b-form-group> </b-form-group>
<b-form-group
label="Prometheus installation method."
description="What query format is used to fetch metrics from Prometheus"
>
<b-form-select
v-model="prometheusProvider"
:options="prometheusProviders"
@change="onPrometheusProviderSave"
/>
</b-form-group>
</div> </div>
</div> </div>
<div class="col-12"> <div class="col-12">
@ -57,6 +67,8 @@
<script> <script>
import { lstatSync } from "fs" import { lstatSync } from "fs"
import { prometheusProviders } from '../../../../common/prometheus-providers';
export default { export default {
name: 'ClusterSettingsPreferences', name: 'ClusterSettingsPreferences',
props: { props: {
@ -70,18 +82,31 @@ export default {
errors: { errors: {
terminalcwd: null terminalcwd: null
}, },
prometheusPath: "" prometheusPath: "",
prometheusProvider: "",
prometheusProviders: [],
} }
}, },
mounted: function() { mounted: async function() {
this.prometheusProviders = prometheusProviders.map((provider) => {
return { text: provider.name, value: provider.id }
})
this.updateValues()
},
methods: {
updateValues: function(){
if (this.cluster.preferences.prometheus) { if (this.cluster.preferences.prometheus) {
const prom = this.cluster.preferences.prometheus; const prom = this.cluster.preferences.prometheus;
this.prometheusPath = `${prom.namespace}/${prom.service}:${prom.port}` this.prometheusPath = `${prom.namespace}/${prom.service}:${prom.port}`
} else {
this.prometheusPath = ""
}
if (this.cluster.preferences.prometheusProvider) {
this.prometheusProvider = this.cluster.preferences.prometheusProvider.type
} else {
this.prometheusProvider = "lens"
} }
}, },
computed: {
},
methods: {
parsePrometheusPath: function(path) { parsePrometheusPath: function(path) {
let parsed = path.split(/\/|:/) let parsed = path.split(/\/|:/)
return { return {
@ -121,10 +146,22 @@ export default {
} }
this.$store.dispatch("storeCluster", this.cluster); this.$store.dispatch("storeCluster", this.cluster);
}, },
onPrometheusProviderSave: function() {
if (this.prometheusProvider === "") {
this.cluster.preferences.prometheusProvider = null;
} else {
this.cluster.preferences.prometheusProvider = { type: this.prometheusProvider }
}
this.$store.dispatch("storeCluster", this.cluster);
},
onTerminalCwdSave: function() { onTerminalCwdSave: function() {
if(this.cluster.preferences.terminalCWD === "") this.cluster.preferences.terminalCWD = null if(this.cluster.preferences.terminalCWD === "") this.cluster.preferences.terminalCWD = null
this.$store.dispatch("storeCluster", this.cluster); this.$store.dispatch("storeCluster", this.cluster);
} },
},
watch: {
"cluster": "updateValues",
} }
} }
</script> </script>