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:
parent
29cd171d0f
commit
724cf0c687
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
|
|
||||||
export type IMetricsQuery = string | string[] | {
|
export type IMetricsQuery = string | string[] | {
|
||||||
[metricName: string]: string;
|
[metricName: string]: string | object;
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/common/prometheus-providers.ts
Normal file
11
src/common/prometheus-providers.ts
Normal 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()
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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") !== "") {
|
||||||
|
|||||||
10
src/main/prometheus/helm.ts
Normal file
10
src/main/prometheus/helm.ts
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/main/prometheus/lens.ts
Normal file
66
src/main/prometheus/lens.ts
Normal 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)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/main/prometheus/operator.ts
Normal file
66
src/main/prometheus/operator.ts
Normal 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)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/main/prometheus/provider-registry.ts
Normal file
76
src/main/prometheus/provider-registry.ts
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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];
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user