mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Introduce injectable forms of PrometheusProviders
- Remove class requirement - Make everything injectable Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
fad632c6ed
commit
a5873bbf7a
@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { PrometheusProvider, PrometheusService, PrometheusProviderRegistry } from "../prometheus/provider-registry";
|
||||
import type { PrometheusProvider, PrometheusService } from "../prometheus/provider";
|
||||
import type { ClusterPrometheusPreferences } from "../../common/cluster-types";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type httpProxy from "http-proxy";
|
||||
@ -13,6 +13,8 @@ import { CoreV1Api } from "@kubernetes/client-node";
|
||||
import logger from "../logger";
|
||||
import type { KubeAuthProxy } from "../kube-auth-proxy/kube-auth-proxy";
|
||||
import type { CreateKubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||
import type { GetPrometheusProviderByKind } from "../prometheus/get-by-kind.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
|
||||
export interface PrometheusDetails {
|
||||
prometheusPath: string;
|
||||
@ -26,10 +28,11 @@ interface PrometheusServicePreferences {
|
||||
prefix: string;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
readonly createKubeAuthProxy: CreateKubeAuthProxy;
|
||||
export interface ContextHandlerDependencies {
|
||||
createKubeAuthProxy: CreateKubeAuthProxy;
|
||||
getPrometheusProviderByKind: GetPrometheusProviderByKind;
|
||||
readonly authProxyCa: string;
|
||||
readonly prometheusProviderRegistry: PrometheusProviderRegistry;
|
||||
readonly prometheusProviders: IComputedValue<PrometheusProvider[]>;
|
||||
}
|
||||
|
||||
export interface ClusterContextHandler {
|
||||
@ -51,7 +54,7 @@ export class ContextHandler implements ClusterContextHandler {
|
||||
protected prometheusProvider?: string;
|
||||
protected prometheus?: PrometheusServicePreferences;
|
||||
|
||||
constructor(private dependencies: Dependencies, protected cluster: Cluster) {
|
||||
constructor(private readonly dependencies: ContextHandlerDependencies, protected readonly cluster: Cluster) {
|
||||
this.clusterUrl = url.parse(cluster.apiUrl);
|
||||
this.setupPrometheus(cluster.preferences);
|
||||
}
|
||||
@ -75,22 +78,21 @@ export class ContextHandler implements ClusterContextHandler {
|
||||
|
||||
protected ensurePrometheusProvider(service: PrometheusService): PrometheusProvider {
|
||||
if (!this.prometheusProvider) {
|
||||
logger.info(`[CONTEXT-HANDLER]: using ${service.id} as prometheus provider for clusterId=${this.cluster.id}`);
|
||||
this.prometheusProvider = service.id;
|
||||
logger.info(`[CONTEXT-HANDLER]: using ${service.kind} as prometheus provider for clusterId=${this.cluster.id}`);
|
||||
this.prometheusProvider = service.kind;
|
||||
}
|
||||
|
||||
return this.dependencies.prometheusProviderRegistry.getByKind(this.prometheusProvider);
|
||||
return this.dependencies.getPrometheusProviderByKind(this.prometheusProvider);
|
||||
}
|
||||
|
||||
protected listPotentialProviders(): PrometheusProvider[] {
|
||||
const registry = this.dependencies.prometheusProviderRegistry;
|
||||
const provider = this.prometheusProvider && registry.getByKind(this.prometheusProvider);
|
||||
const provider = this.prometheusProvider && this.dependencies.getPrometheusProviderByKind(this.prometheusProvider);
|
||||
|
||||
if (provider) {
|
||||
return [provider];
|
||||
}
|
||||
|
||||
return Array.from(registry.providers.values());
|
||||
return this.dependencies.prometheusProviders.get();
|
||||
}
|
||||
|
||||
protected async getPrometheusService(): Promise<PrometheusService> {
|
||||
@ -98,7 +100,7 @@ export class ContextHandler implements ClusterContextHandler {
|
||||
|
||||
if (this.prometheus && this.prometheusProvider) {
|
||||
return {
|
||||
id: this.prometheusProvider,
|
||||
kind: this.prometheusProvider,
|
||||
namespace: this.prometheus.namespace,
|
||||
service: this.prometheus.service,
|
||||
port: this.prometheus.port,
|
||||
|
||||
@ -3,32 +3,32 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import selfsigned from "selfsigned";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { ClusterContextHandler } from "./context-handler";
|
||||
import type { ClusterContextHandler, ContextHandlerDependencies } from "./context-handler";
|
||||
import { ContextHandler } from "./context-handler";
|
||||
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||
import { getKubeAuthProxyCertificate } from "../kube-auth-proxy/get-kube-auth-proxy-certificate";
|
||||
import kubeAuthProxyCertificateInjectable from "../kube-auth-proxy/kube-auth-proxy-certificate.injectable";
|
||||
import URLParse from "url-parse";
|
||||
import prometheusProviderRegistryInjectable from "../prometheus/prometheus-provider-registry.injectable";
|
||||
import getPrometheusProviderByKindInjectable from "../prometheus/get-by-kind.injectable";
|
||||
import prometheusProvidersInjectable from "../prometheus/providers.injectable";
|
||||
|
||||
const createContextHandlerInjectable = getInjectable({
|
||||
id: "create-context-handler",
|
||||
|
||||
instantiate: (di) => {
|
||||
const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);
|
||||
const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable);
|
||||
const dependencies: Omit<ContextHandlerDependencies, "authProxyCa"> = {
|
||||
createKubeAuthProxy: di.inject(createKubeAuthProxyInjectable),
|
||||
getPrometheusProviderByKind: di.inject(getPrometheusProviderByKindInjectable),
|
||||
prometheusProviders: di.inject(prometheusProvidersInjectable),
|
||||
};
|
||||
|
||||
return (cluster: Cluster): ClusterContextHandler => {
|
||||
const clusterUrl = new URLParse(cluster.apiUrl);
|
||||
|
||||
const dependencies = {
|
||||
createKubeAuthProxy,
|
||||
prometheusProviderRegistry,
|
||||
authProxyCa: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate).cert,
|
||||
};
|
||||
|
||||
return new ContextHandler(dependencies, cluster);
|
||||
return new ContextHandler({
|
||||
...dependencies,
|
||||
authProxyCa: di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname).cert,
|
||||
}, cluster);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
29
src/main/prometheus/get-by-kind.injectable.ts
Normal file
29
src/main/prometheus/get-by-kind.injectable.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { matches } from "lodash/fp";
|
||||
import type { PrometheusProvider } from "./provider";
|
||||
import prometheusProvidersInjectable from "./providers.injectable";
|
||||
|
||||
export type GetPrometheusProviderByKind = (kind: string) => PrometheusProvider;
|
||||
|
||||
const getPrometheusProviderByKindInjectable = getInjectable({
|
||||
id: "get-prometheus-provider-by-kind",
|
||||
instantiate: (di): GetPrometheusProviderByKind => {
|
||||
const providers = di.inject(prometheusProvidersInjectable);
|
||||
|
||||
return (kind) => {
|
||||
const provider = providers.get().find(matches({ kind }));
|
||||
|
||||
if (!provider) {
|
||||
throw new Error(`Provider of kind "${kind}" does not exist`);
|
||||
}
|
||||
|
||||
return provider;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default getPrometheusProviderByKindInjectable;
|
||||
23
src/main/prometheus/helm-14-provider.injectable.ts
Normal file
23
src/main/prometheus/helm-14-provider.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getLensLikeQueryFor } from "./lens-provider.injectable";
|
||||
import { createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const helm14PrometheusProviderInjectable = getInjectable({
|
||||
id: "helm14-prometheus-provider",
|
||||
instantiate: () => createPrometheusProvider({
|
||||
kind: "helm14",
|
||||
name: "Helm 14.x",
|
||||
isConfigurable: true,
|
||||
getQuery: getLensLikeQueryFor({ rateAccuracy: "5m" }),
|
||||
getService: (client) => findFirstNamespacedService(client, "app=prometheus,component=server,heritage=Helm"),
|
||||
}),
|
||||
injectionToken: prometheusProviderInjectionToken,
|
||||
});
|
||||
|
||||
export default helm14PrometheusProviderInjectable;
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { PrometheusLens } from "./lens";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import type { PrometheusService } from "./provider-registry";
|
||||
import { isRequestError } from "../../common/utils";
|
||||
|
||||
export class PrometheusHelm14 extends PrometheusLens {
|
||||
readonly id: string = "helm14";
|
||||
readonly name: string = "Helm 14.x";
|
||||
readonly rateAccuracy: string = "5m";
|
||||
readonly isConfigurable: boolean = true;
|
||||
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
try {
|
||||
const selector = "app=prometheus,component=server,heritage=Helm";
|
||||
const { body: { items: [service] }} = await client.listServiceForAllNamespaces(undefined, undefined, undefined, selector);
|
||||
|
||||
if (service?.metadata?.namespace && service.metadata.name && service.spec?.ports && service.metadata?.labels?.chart?.startsWith("prometheus-14")) {
|
||||
return {
|
||||
id: this.id,
|
||||
namespace: service.metadata.namespace,
|
||||
service: service.metadata.name,
|
||||
port: service.spec.ports[0].port,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to list services for Prometheus ${this.name} in all namespaces: ${isRequestError(error) ? error.response?.body.message : error}`);
|
||||
}
|
||||
|
||||
throw new Error(`No service found for Prometheus ${this.name} from any namespace`);
|
||||
}
|
||||
}
|
||||
@ -3,20 +3,12 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { inspect } from "util";
|
||||
import { PrometheusProvider, type PrometheusService } from "./provider-registry";
|
||||
export class PrometheusHelm extends PrometheusProvider {
|
||||
readonly id: string = "helm";
|
||||
readonly name: string = "Helm";
|
||||
readonly rateAccuracy: string = "5m";
|
||||
readonly isConfigurable: boolean = true;
|
||||
import type { PrometheusProvider } from "./provider";
|
||||
import { createPrometheusProvider, bytesSent, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
return this.getFirstNamespacedService(client, "app=prometheus,component=server,heritage=Helm");
|
||||
}
|
||||
|
||||
public getQuery(opts: Record<string, string>, queryName: string): string {
|
||||
export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
|
||||
(opts, queryName) => {
|
||||
switch(opts.category) {
|
||||
case "cluster":
|
||||
switch (queryName) {
|
||||
@ -33,7 +25,7 @@ export class PrometheusHelm extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="memory"}) by (component)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`;
|
||||
return `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${rateAccuracy}]))`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`;
|
||||
case "cpuLimits":
|
||||
@ -65,7 +57,7 @@ export class PrometheusHelm extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="memory"}) by (node)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(node)`;
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${rateAccuracy}])) by(node)`;
|
||||
case "cpuCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`;
|
||||
case "cpuAllocatableCapacity":
|
||||
@ -79,7 +71,7 @@ export class PrometheusHelm extends PrometheusProvider {
|
||||
case "pods":
|
||||
switch (queryName) {
|
||||
case "cpuUsage":
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "cpuLimits":
|
||||
@ -93,13 +85,13 @@ export class PrometheusHelm extends PrometheusProvider {
|
||||
case "fsUsage":
|
||||
return `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "fsWrites":
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "fsReads":
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkReceive":
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkTransmit":
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
}
|
||||
break;
|
||||
case "pvc":
|
||||
@ -113,17 +105,42 @@ export class PrometheusHelm extends PrometheusProvider {
|
||||
case "ingress":
|
||||
switch (queryName) {
|
||||
case "bytesSentSuccess":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^2\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^2\\\\d*",
|
||||
});
|
||||
case "bytesSentFailure":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^5\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^5\\\\d*",
|
||||
});
|
||||
case "requestDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
case "responseDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown query name ${inspect(queryName, false, undefined, false)} for category: ${inspect(opts.category, false, undefined, false)}`);
|
||||
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const helmPrometheusProviderInjectable = getInjectable({
|
||||
id: "helm-prometheus-provider",
|
||||
instantiate: () => createPrometheusProvider({
|
||||
kind: "helm",
|
||||
name: "Helm",
|
||||
isConfigurable: true,
|
||||
getQuery: getHelmLikeQueryFor({ rateAccuracy: "5m" }),
|
||||
getService: (client) => findFirstNamespacedService(client, "app=prometheus,component=server,heritage=Helm"),
|
||||
}),
|
||||
injectionToken: prometheusProviderInjectionToken,
|
||||
});
|
||||
|
||||
export default helmPrometheusProviderInjectable;
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./provider-registry";
|
||||
@ -3,22 +3,12 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { PrometheusService } from "./provider-registry";
|
||||
import { PrometheusProvider } from "./provider-registry";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { inspect } from "util";
|
||||
import { bytesSent, prometheusProviderInjectionToken, findNamespacedService, createPrometheusProvider } from "./provider";
|
||||
import type { PrometheusProvider } from "./provider";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
export class PrometheusLens extends PrometheusProvider {
|
||||
readonly id: string = "lens";
|
||||
readonly name: string = "Lens";
|
||||
readonly rateAccuracy: string = "1m";
|
||||
readonly isConfigurable: boolean = false;
|
||||
|
||||
public getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
return this.getNamespacedService(client, "prometheus", "lens-metrics");
|
||||
}
|
||||
|
||||
public getQuery(opts: Record<string, string>, queryName: string): string {
|
||||
export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
|
||||
(opts, queryName) => {
|
||||
switch(opts.category) {
|
||||
case "cluster":
|
||||
switch (queryName) {
|
||||
@ -35,7 +25,7 @@ export class PrometheusLens extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="memory"}) by (component)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{kubernetes_node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`;
|
||||
return `sum(rate(node_cpu_seconds_total{kubernetes_node=~"${opts.nodes}", mode=~"user|system"}[${rateAccuracy}]))`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`;
|
||||
case "cpuLimits":
|
||||
@ -67,7 +57,7 @@ export class PrometheusLens extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="memory"}) by (node)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(kubernetes_node)`;
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${rateAccuracy}])) by(kubernetes_node)`;
|
||||
case "cpuCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`;
|
||||
case "cpuAllocatableCapacity":
|
||||
@ -81,7 +71,7 @@ export class PrometheusLens extends PrometheusProvider {
|
||||
case "pods":
|
||||
switch (queryName) {
|
||||
case "cpuUsage":
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "cpuLimits":
|
||||
@ -95,13 +85,13 @@ export class PrometheusLens extends PrometheusProvider {
|
||||
case "fsUsage":
|
||||
return `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "fsWrites":
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "fsReads":
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkReceive":
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkTransmit":
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
}
|
||||
break;
|
||||
case "pvc":
|
||||
@ -115,17 +105,42 @@ export class PrometheusLens extends PrometheusProvider {
|
||||
case "ingress":
|
||||
switch (queryName) {
|
||||
case "bytesSentSuccess":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^2\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^2\\\\d*",
|
||||
});
|
||||
case "bytesSentFailure":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^5\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^5\\\\d*",
|
||||
});
|
||||
case "requestDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
case "responseDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown query name ${inspect(queryName, false, undefined, false)} for category: ${inspect(opts.category, false, undefined, false)}`);
|
||||
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const lensPrometheusProviderInjectable = getInjectable({
|
||||
id: "lens-prometheus-provider",
|
||||
instantiate: () => createPrometheusProvider({
|
||||
kind: "lens",
|
||||
name: "Lens",
|
||||
isConfigurable: false,
|
||||
getQuery: getLensLikeQueryFor({ rateAccuracy: "1m" }),
|
||||
getService: (client) => findNamespacedService(client, "prometheus", "lens-metrics"),
|
||||
}),
|
||||
injectionToken: prometheusProviderInjectionToken,
|
||||
});
|
||||
|
||||
export default lensPrometheusProviderInjectable;
|
||||
|
||||
@ -3,22 +3,12 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { PrometheusService } from "./provider-registry";
|
||||
import { PrometheusProvider } from "./provider-registry";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { inspect } from "util";
|
||||
import type { PrometheusProvider } from "./provider";
|
||||
import { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
export class PrometheusOperator extends PrometheusProvider {
|
||||
readonly rateAccuracy: string = "1m";
|
||||
readonly id: string = "operator";
|
||||
readonly name: string = "Prometheus Operator";
|
||||
readonly isConfigurable: boolean = true;
|
||||
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
return this.getFirstNamespacedService(client, "operated-prometheus=true");
|
||||
}
|
||||
|
||||
public getQuery(opts: Record<string, string>, queryName: string): string {
|
||||
export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
|
||||
(opts, queryName) => {
|
||||
switch(opts.category) {
|
||||
case "cluster":
|
||||
switch (queryName) {
|
||||
@ -35,7 +25,7 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="memory"})`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`;
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"})`;
|
||||
case "cpuLimits":
|
||||
@ -67,7 +57,7 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="memory"}) by (node)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}]) * on (pod, namespace) group_left(node) kube_pod_info) by (node)`;
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${rateAccuracy}]) * on (pod, namespace) group_left(node) kube_pod_info) by (node)`;
|
||||
case "cpuCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`;
|
||||
case "cpuAllocatableCapacity":
|
||||
@ -81,7 +71,7 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "pods":
|
||||
switch (queryName) {
|
||||
case "cpuUsage":
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="", image!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="", image!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "cpuLimits":
|
||||
@ -95,13 +85,13 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "fsUsage":
|
||||
return `sum(container_fs_usage_bytes{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "fsWrites":
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "fsReads":
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkReceive":
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkTransmit":
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
}
|
||||
break;
|
||||
case "pvc":
|
||||
@ -115,17 +105,42 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "ingress":
|
||||
switch (queryName) {
|
||||
case "bytesSentSuccess":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^2\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^2\\\\d*",
|
||||
});
|
||||
case "bytesSentFailure":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^5\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^5\\\\d*",
|
||||
});
|
||||
case "requestDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
case "responseDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown query name ${inspect(queryName, false, undefined, false)} for category: ${inspect(opts.category, false, undefined, false)}`);
|
||||
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const operatorPrometheusProviderInjectable = getInjectable({
|
||||
id: "operator-prometheus-provider",
|
||||
instantiate: () => createPrometheusProvider({
|
||||
kind: "operator",
|
||||
name: "Prometheus Operator",
|
||||
isConfigurable: true,
|
||||
getService: (client) => findFirstNamespacedService(client, "operated-prometheus=true"),
|
||||
getQuery: getOperatorLikeQueryFor({ rateAccuracy: "1m" }),
|
||||
}),
|
||||
injectionToken: prometheusProviderInjectionToken,
|
||||
});
|
||||
|
||||
export default operatorPrometheusProviderInjectable;
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { PrometheusProviderRegistry } from "./provider-registry";
|
||||
|
||||
const prometheusProviderRegistryInjectable = getInjectable({
|
||||
id: "prometheus-provider-registry",
|
||||
instantiate: () => new PrometheusProviderRegistry(),
|
||||
});
|
||||
|
||||
export default prometheusProviderRegistryInjectable;
|
||||
@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { isRequestError } from "../../common/utils";
|
||||
|
||||
export interface PrometheusService {
|
||||
id: string;
|
||||
namespace: string;
|
||||
service: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export abstract class PrometheusProvider {
|
||||
abstract readonly id: string;
|
||||
abstract readonly name: string;
|
||||
abstract readonly rateAccuracy: string;
|
||||
abstract readonly isConfigurable: boolean;
|
||||
|
||||
abstract getQuery(opts: Record<string, string>, queryName: string): string;
|
||||
abstract getPrometheusService(client: CoreV1Api): Promise<PrometheusService | undefined>;
|
||||
|
||||
protected bytesSent(ingress: string, namespace: string, statuses: string): string {
|
||||
return `sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}",namespace="${namespace}",status=~"${statuses}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
}
|
||||
|
||||
protected async getFirstNamespacedService(client: CoreV1Api, ...selectors: string[]): Promise<PrometheusService> {
|
||||
try {
|
||||
for (const selector of selectors) {
|
||||
const { body: { items: [service] }} = await client.listServiceForAllNamespaces(undefined, undefined, undefined, selector);
|
||||
|
||||
if (service?.metadata?.namespace && service.metadata.name && service.spec?.ports) {
|
||||
return {
|
||||
id: this.id,
|
||||
namespace: service.metadata.namespace,
|
||||
service: service.metadata.name,
|
||||
port: service.spec.ports[0].port,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to list services for Prometheus${this.name} in all namespaces: ${isRequestError(error) ? error.response?.body.message : error}`);
|
||||
}
|
||||
|
||||
throw new Error(`No service found for Prometheus${this.name} from any namespace`);
|
||||
}
|
||||
|
||||
protected async getNamespacedService(client: CoreV1Api, name: string, namespace: string): Promise<PrometheusService> {
|
||||
try {
|
||||
const { body: service } = await client.readNamespacedService(name, namespace);
|
||||
|
||||
if (!service.metadata?.namespace || !service.metadata.name || !service.spec?.ports) {
|
||||
throw new Error(`Service returned from Prometheus${this.name} in namespace="${namespace}" did not have required information`);
|
||||
}
|
||||
|
||||
return {
|
||||
id: this.id,
|
||||
namespace: service.metadata.namespace,
|
||||
service: service.metadata.name,
|
||||
port: service.spec.ports[0].port,
|
||||
};
|
||||
} catch(error) {
|
||||
throw new Error(`Failed to list services for Prometheus${this.name} in namespace="${namespace}": ${isRequestError(error) ? error.response?.body.message : error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PrometheusProviderRegistry {
|
||||
public providers = new Map<string, PrometheusProvider>();
|
||||
|
||||
getByKind(kind: string): PrometheusProvider {
|
||||
const provider = this.providers.get(kind);
|
||||
|
||||
if (!provider) {
|
||||
throw new Error("Unknown Prometheus provider");
|
||||
}
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
registerProvider(provider: PrometheusProvider): this {
|
||||
if (this.providers.has(provider.id)) {
|
||||
throw new Error("Provider already registered under that kind");
|
||||
}
|
||||
|
||||
this.providers.set(provider.id, provider);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
103
src/main/prometheus/provider.ts
Normal file
103
src/main/prometheus/provider.ts
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { isRequestError } from "../../common/utils";
|
||||
|
||||
export interface PrometheusService extends PrometheusServiceInfo {
|
||||
kind: string;
|
||||
}
|
||||
|
||||
export interface PrometheusServiceInfo {
|
||||
namespace: string;
|
||||
service: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export interface PrometheusProvider {
|
||||
readonly kind: string;
|
||||
readonly name: string;
|
||||
readonly isConfigurable: boolean;
|
||||
|
||||
getQuery(opts: Record<string, string>, queryName: string): string;
|
||||
getPrometheusService(client: CoreV1Api): Promise<PrometheusService>;
|
||||
}
|
||||
|
||||
export interface CreatePrometheusProviderOpts {
|
||||
readonly kind: string;
|
||||
readonly name: string;
|
||||
readonly isConfigurable: boolean;
|
||||
|
||||
getQuery(opts: Record<string, string>, queryName: string): string;
|
||||
getService(client: CoreV1Api): Promise<PrometheusServiceInfo>;
|
||||
}
|
||||
|
||||
export const createPrometheusProvider = ({ getService, ...opts }: CreatePrometheusProviderOpts): PrometheusProvider => ({
|
||||
...opts,
|
||||
getPrometheusService: async (client) => {
|
||||
try {
|
||||
return {
|
||||
kind: opts.kind,
|
||||
...await getService(client),
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to find Prometheus provider for "${opts.name}"`, { cause: error });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export async function findFirstNamespacedService(client: CoreV1Api, ...selectors: string[]): Promise<PrometheusServiceInfo> {
|
||||
try {
|
||||
for (const selector of selectors) {
|
||||
const { body: { items: [service] }} = await client.listServiceForAllNamespaces(undefined, undefined, undefined, selector);
|
||||
|
||||
if (service?.metadata?.namespace && service.metadata.name && service.spec?.ports) {
|
||||
return {
|
||||
namespace: service.metadata.namespace,
|
||||
service: service.metadata.name,
|
||||
port: service.spec.ports[0].port,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to list services in all namespaces: ${isRequestError(error) ? error.response?.body.message : error}`);
|
||||
}
|
||||
|
||||
throw new Error(`No service found from any namespace`);
|
||||
}
|
||||
|
||||
export async function findNamespacedService(client: CoreV1Api, name: string, namespace: string): Promise<PrometheusServiceInfo> {
|
||||
try {
|
||||
const { body: service } = await client.readNamespacedService(name, namespace);
|
||||
|
||||
if (!service.metadata?.namespace || !service.metadata.name || !service.spec?.ports) {
|
||||
throw new Error(`Service found in namespace="${namespace}" did not have required information`);
|
||||
}
|
||||
|
||||
return {
|
||||
namespace: service.metadata.namespace,
|
||||
service: service.metadata.name,
|
||||
port: service.spec.ports[0].port,
|
||||
};
|
||||
} catch(error) {
|
||||
throw new Error(`Failed to list services in namespace="${namespace}": ${isRequestError(error) ? error.response?.body.message : error}`);
|
||||
}
|
||||
}
|
||||
|
||||
export interface BytesSentArgs {
|
||||
rateAccuracy: string;
|
||||
ingress: string;
|
||||
namespace: string;
|
||||
statuses: string;
|
||||
}
|
||||
|
||||
export function bytesSent({ rateAccuracy, ingress, namespace, statuses }: BytesSentArgs): string {
|
||||
return `sum(rate(nginx_ingress_controller_bytes_sent_sum{ingress="${ingress}",namespace="${namespace}",status=~"${statuses}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
}
|
||||
|
||||
export const prometheusProviderInjectionToken = getInjectionToken<PrometheusProvider>({
|
||||
id: "prometheus-provider",
|
||||
});
|
||||
18
src/main/prometheus/providers.injectable.ts
Normal file
18
src/main/prometheus/providers.injectable.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import { prometheusProviderInjectionToken } from "./provider";
|
||||
|
||||
const prometheusProvidersInjectable = getInjectable({
|
||||
id: "prometheus-providers",
|
||||
instantiate: (di) => {
|
||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||
|
||||
return computedInjectMany(prometheusProviderInjectionToken);
|
||||
},
|
||||
});
|
||||
|
||||
export default prometheusProvidersInjectable;
|
||||
@ -3,22 +3,12 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { PrometheusService } from "./provider-registry";
|
||||
import { PrometheusProvider } from "./provider-registry";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { inspect } from "util";
|
||||
import type { PrometheusProvider } from "./provider";
|
||||
import { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
export class PrometheusStacklight extends PrometheusProvider {
|
||||
readonly id: string = "stacklight";
|
||||
readonly name: string = "Stacklight";
|
||||
readonly rateAccuracy: string = "1m";
|
||||
readonly isConfigurable: boolean = true;
|
||||
|
||||
public getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
return this.getNamespacedService(client, "prometheus-server", "stacklight");
|
||||
}
|
||||
|
||||
public getQuery(opts: Record<string, string>, queryName: string): string {
|
||||
export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
|
||||
(opts, queryName) => {
|
||||
switch(opts.category) {
|
||||
case "cluster":
|
||||
switch (queryName) {
|
||||
@ -35,7 +25,7 @@ export class PrometheusStacklight extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="memory"}) by (component)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`;
|
||||
return `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${rateAccuracy}]))`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"}) by (component)`;
|
||||
case "cpuLimits":
|
||||
@ -67,7 +57,7 @@ export class PrometheusStacklight extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="memory"}) by (node)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(node)`;
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${rateAccuracy}])) by(node)`;
|
||||
case "cpuCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`;
|
||||
case "cpuAllocatableCapacity":
|
||||
@ -81,7 +71,7 @@ export class PrometheusStacklight extends PrometheusProvider {
|
||||
case "pods":
|
||||
switch (queryName) {
|
||||
case "cpuUsage":
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "cpuLimits":
|
||||
@ -95,13 +85,13 @@ export class PrometheusStacklight extends PrometheusProvider {
|
||||
case "fsUsage":
|
||||
return `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`;
|
||||
case "fsWrites":
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_writes_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "fsReads":
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_fs_reads_bytes_total{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkReceive":
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
case "networkTransmit":
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`;
|
||||
return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
|
||||
}
|
||||
break;
|
||||
case "pvc":
|
||||
@ -115,17 +105,42 @@ export class PrometheusStacklight extends PrometheusProvider {
|
||||
case "ingress":
|
||||
switch (queryName) {
|
||||
case "bytesSentSuccess":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^2\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^2\\\\d*",
|
||||
});
|
||||
case "bytesSentFailure":
|
||||
return this.bytesSent(opts.ingress, opts.namespace, "^5\\\\d*");
|
||||
return bytesSent({
|
||||
rateAccuracy,
|
||||
ingress: opts.ingress,
|
||||
namespace: opts.namespace,
|
||||
statuses: "^5\\\\d*",
|
||||
});
|
||||
case "requestDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
case "responseDurationSeconds":
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`;
|
||||
return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (ingress, namespace)`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown query name ${inspect(queryName, false, undefined, false)} for category: ${inspect(opts.category, false, undefined, false)}`);
|
||||
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const stacklightPrometheusProviderInjectable = getInjectable({
|
||||
id: "stacklight-prometheus-provider",
|
||||
instantiate: () => createPrometheusProvider({
|
||||
kind: "stacklight",
|
||||
name: "Stacklight",
|
||||
isConfigurable: true,
|
||||
getService: (client) => findFirstNamespacedService(client, "prometheus-server", "stacklight"),
|
||||
getQuery: getStacklightLikeQueryFor({ rateAccuracy: "1m" }),
|
||||
}),
|
||||
injectionToken: prometheusProviderInjectionToken,
|
||||
});
|
||||
|
||||
export default stacklightPrometheusProviderInjectable;
|
||||
|
||||
@ -65,7 +65,7 @@ const addMetricsRouteInjectable = getRouteInjectable({
|
||||
try {
|
||||
const { prometheusPath, provider } = await cluster.contextHandler.getPrometheusDetails();
|
||||
|
||||
prometheusMetadata.provider = provider?.id;
|
||||
prometheusMetadata.provider = provider?.kind;
|
||||
prometheusMetadata.autoDetected = !cluster.preferences.prometheusProvider?.type;
|
||||
|
||||
if (!prometheusPath) {
|
||||
|
||||
@ -6,23 +6,22 @@
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import { getRouteInjectable } from "../../router/router.injectable";
|
||||
import { route } from "../../router/route";
|
||||
import prometheusProviderRegistryInjectable from "../../prometheus/prometheus-provider-registry.injectable";
|
||||
import prometheusProvidersInjectable from "../../prometheus/providers.injectable";
|
||||
|
||||
const getMetricProvidersRouteInjectable = getRouteInjectable({
|
||||
id: "get-metric-providers-route",
|
||||
|
||||
instantiate: (di) => {
|
||||
const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable);
|
||||
const prometheusProviders = di.inject(prometheusProvidersInjectable);
|
||||
|
||||
return route({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/metrics/providers`,
|
||||
})(() => ({
|
||||
response: Array.from(
|
||||
prometheusProviderRegistry
|
||||
.providers
|
||||
.values(),
|
||||
({ name, id, isConfigurable }) => ({ name, id, isConfigurable }),
|
||||
response: (
|
||||
prometheusProviders
|
||||
.get()
|
||||
.map(({ name, kind: id, isConfigurable }) => ({ name, id, isConfigurable }))
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { PrometheusLens } from "../../prometheus/lens";
|
||||
import { PrometheusHelm } from "../../prometheus/helm";
|
||||
import { PrometheusHelm14 } from "../../prometheus/helm-14";
|
||||
import { PrometheusOperator } from "../../prometheus/operator";
|
||||
import { PrometheusStacklight } from "../../prometheus/stacklight";
|
||||
import prometheusProviderRegistryInjectable from "../../prometheus/prometheus-provider-registry.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token";
|
||||
|
||||
const setupPrometheusRegistryInjectable = getInjectable({
|
||||
id: "setup-prometheus-registry",
|
||||
|
||||
instantiate: (di) => {
|
||||
const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable);
|
||||
|
||||
return {
|
||||
id: "setup-prometheus-registry",
|
||||
run: () => {
|
||||
prometheusProviderRegistry
|
||||
.registerProvider(new PrometheusLens())
|
||||
.registerProvider(new PrometheusHelm14())
|
||||
.registerProvider(new PrometheusHelm())
|
||||
.registerProvider(new PrometheusOperator())
|
||||
.registerProvider(new PrometheusStacklight());
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupPrometheusRegistryInjectable;
|
||||
Loading…
Reference in New Issue
Block a user