From a5873bbf7a87906b3995309cdbcf91f3df4215e3 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 16 Nov 2022 11:56:04 -0500 Subject: [PATCH] Introduce injectable forms of PrometheusProviders - Remove class requirement - Make everything injectable Signed-off-by: Sebastian Malton --- src/main/context-handler/context-handler.ts | 26 +++-- .../create-context-handler.injectable.ts | 26 ++--- src/main/prometheus/get-by-kind.injectable.ts | 29 +++++ .../prometheus/helm-14-provider.injectable.ts | 23 ++++ src/main/prometheus/helm-14.ts | 36 ------ .../{helm.ts => helm-provider.injectable.ts} | 69 +++++++----- src/main/prometheus/index.ts | 6 - .../{lens.ts => lens-provider.injectable.ts} | 71 +++++++----- ....ts => operator-provider.injectable.ts.ts} | 71 +++++++----- ...prometheus-provider-registry.injectable.ts | 13 --- src/main/prometheus/provider-registry.ts | 92 ---------------- src/main/prometheus/provider.ts | 103 ++++++++++++++++++ src/main/prometheus/providers.injectable.ts | 18 +++ ...t.ts => stacklight-provider.injectable.ts} | 71 +++++++----- .../metrics/add-metrics-route.injectable.ts | 2 +- .../get-metric-providers-route.injectable.ts | 13 +-- .../setup-prometheus-registry.injectable.ts | 36 ------ 17 files changed, 379 insertions(+), 326 deletions(-) create mode 100644 src/main/prometheus/get-by-kind.injectable.ts create mode 100644 src/main/prometheus/helm-14-provider.injectable.ts delete mode 100644 src/main/prometheus/helm-14.ts rename src/main/prometheus/{helm.ts => helm-provider.injectable.ts} (78%) delete mode 100644 src/main/prometheus/index.ts rename src/main/prometheus/{lens.ts => lens-provider.injectable.ts} (78%) rename src/main/prometheus/{operator.ts => operator-provider.injectable.ts.ts} (77%) delete mode 100644 src/main/prometheus/prometheus-provider-registry.injectable.ts delete mode 100644 src/main/prometheus/provider-registry.ts create mode 100644 src/main/prometheus/provider.ts create mode 100644 src/main/prometheus/providers.injectable.ts rename src/main/prometheus/{stacklight.ts => stacklight-provider.injectable.ts} (77%) delete mode 100644 src/main/start-main-application/runnables/setup-prometheus-registry.injectable.ts diff --git a/src/main/context-handler/context-handler.ts b/src/main/context-handler/context-handler.ts index 563c7afecc..a603af964d 100644 --- a/src/main/context-handler/context-handler.ts +++ b/src/main/context-handler/context-handler.ts @@ -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; } 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 { @@ -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, diff --git a/src/main/context-handler/create-context-handler.injectable.ts b/src/main/context-handler/create-context-handler.injectable.ts index 8185bd7c80..530f6c8385 100644 --- a/src/main/context-handler/create-context-handler.injectable.ts +++ b/src/main/context-handler/create-context-handler.injectable.ts @@ -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 = { + 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); }; }, }); diff --git a/src/main/prometheus/get-by-kind.injectable.ts b/src/main/prometheus/get-by-kind.injectable.ts new file mode 100644 index 0000000000..93d7567f6e --- /dev/null +++ b/src/main/prometheus/get-by-kind.injectable.ts @@ -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; diff --git a/src/main/prometheus/helm-14-provider.injectable.ts b/src/main/prometheus/helm-14-provider.injectable.ts new file mode 100644 index 0000000000..6cc66c519e --- /dev/null +++ b/src/main/prometheus/helm-14-provider.injectable.ts @@ -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; + diff --git a/src/main/prometheus/helm-14.ts b/src/main/prometheus/helm-14.ts deleted file mode 100644 index 7ffac4560b..0000000000 --- a/src/main/prometheus/helm-14.ts +++ /dev/null @@ -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 { - 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`); - } -} diff --git a/src/main/prometheus/helm.ts b/src/main/prometheus/helm-provider.injectable.ts similarity index 78% rename from src/main/prometheus/helm.ts rename to src/main/prometheus/helm-provider.injectable.ts index a2e80cf12e..953bd68277 100644 --- a/src/main/prometheus/helm.ts +++ b/src/main/prometheus/helm-provider.injectable.ts @@ -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 { - return this.getFirstNamespacedService(client, "app=prometheus,component=server,heritage=Helm"); - } - - public getQuery(opts: Record, 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; + diff --git a/src/main/prometheus/index.ts b/src/main/prometheus/index.ts deleted file mode 100644 index 91231ddc43..0000000000 --- a/src/main/prometheus/index.ts +++ /dev/null @@ -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"; diff --git a/src/main/prometheus/lens.ts b/src/main/prometheus/lens-provider.injectable.ts similarity index 78% rename from src/main/prometheus/lens.ts rename to src/main/prometheus/lens-provider.injectable.ts index e651a0a896..c523c72582 100644 --- a/src/main/prometheus/lens.ts +++ b/src/main/prometheus/lens-provider.injectable.ts @@ -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 { - return this.getNamespacedService(client, "prometheus", "lens-metrics"); - } - - public getQuery(opts: Record, 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; + diff --git a/src/main/prometheus/operator.ts b/src/main/prometheus/operator-provider.injectable.ts.ts similarity index 77% rename from src/main/prometheus/operator.ts rename to src/main/prometheus/operator-provider.injectable.ts.ts index 504bb93136..02891d873b 100644 --- a/src/main/prometheus/operator.ts +++ b/src/main/prometheus/operator-provider.injectable.ts.ts @@ -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 { - return this.getFirstNamespacedService(client, "operated-prometheus=true"); - } - - public getQuery(opts: Record, 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; + diff --git a/src/main/prometheus/prometheus-provider-registry.injectable.ts b/src/main/prometheus/prometheus-provider-registry.injectable.ts deleted file mode 100644 index bac9e1a728..0000000000 --- a/src/main/prometheus/prometheus-provider-registry.injectable.ts +++ /dev/null @@ -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; diff --git a/src/main/prometheus/provider-registry.ts b/src/main/prometheus/provider-registry.ts deleted file mode 100644 index c63b22f374..0000000000 --- a/src/main/prometheus/provider-registry.ts +++ /dev/null @@ -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, queryName: string): string; - abstract getPrometheusService(client: CoreV1Api): Promise; - - 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 { - 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 { - 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(); - - 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; - } -} diff --git a/src/main/prometheus/provider.ts b/src/main/prometheus/provider.ts new file mode 100644 index 0000000000..8944c4c0ed --- /dev/null +++ b/src/main/prometheus/provider.ts @@ -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, queryName: string): string; + getPrometheusService(client: CoreV1Api): Promise; +} + +export interface CreatePrometheusProviderOpts { + readonly kind: string; + readonly name: string; + readonly isConfigurable: boolean; + + getQuery(opts: Record, queryName: string): string; + getService(client: CoreV1Api): Promise; +} + +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 { + 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 { + 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({ + id: "prometheus-provider", +}); diff --git a/src/main/prometheus/providers.injectable.ts b/src/main/prometheus/providers.injectable.ts new file mode 100644 index 0000000000..73fe2dfe50 --- /dev/null +++ b/src/main/prometheus/providers.injectable.ts @@ -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; diff --git a/src/main/prometheus/stacklight.ts b/src/main/prometheus/stacklight-provider.injectable.ts similarity index 77% rename from src/main/prometheus/stacklight.ts rename to src/main/prometheus/stacklight-provider.injectable.ts index 0eee912a24..ca7a946466 100644 --- a/src/main/prometheus/stacklight.ts +++ b/src/main/prometheus/stacklight-provider.injectable.ts @@ -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 { - return this.getNamespacedService(client, "prometheus-server", "stacklight"); - } - - public getQuery(opts: Record, 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; + diff --git a/src/main/routes/metrics/add-metrics-route.injectable.ts b/src/main/routes/metrics/add-metrics-route.injectable.ts index e55fc8bec8..03e6980b37 100644 --- a/src/main/routes/metrics/add-metrics-route.injectable.ts +++ b/src/main/routes/metrics/add-metrics-route.injectable.ts @@ -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) { diff --git a/src/main/routes/metrics/get-metric-providers-route.injectable.ts b/src/main/routes/metrics/get-metric-providers-route.injectable.ts index 22b2fbf11b..54dfd9bb95 100644 --- a/src/main/routes/metrics/get-metric-providers-route.injectable.ts +++ b/src/main/routes/metrics/get-metric-providers-route.injectable.ts @@ -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 })) ), })); }, diff --git a/src/main/start-main-application/runnables/setup-prometheus-registry.injectable.ts b/src/main/start-main-application/runnables/setup-prometheus-registry.injectable.ts deleted file mode 100644 index ef755ac7a1..0000000000 --- a/src/main/start-main-application/runnables/setup-prometheus-registry.injectable.ts +++ /dev/null @@ -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;